Attaques par batching GraphQL : 100 requêtes deviennent 10 000 appels à la base de données 📊

Introduction : Le danger caché de la fonctionnalité la plus pratique de GraphQL
GraphQL a révolutionné la façon dont les applications modernes interrogeaient les données, offrant une flexibilité et une efficacité sans précédent. Cependant, cette puissance s’accompagne d’une vulnérabilité de sécurité importante que de nombreux développeurs négligent : les attaques par batching. Ce qui semble être une seule requête HTTP innocente peut silencieusement se transformer en des milliers d’opérations sur la base de données, risquant de mettre à genoux toute votre infrastructure.
Dans ce guide complet, nous explorerons comment les attaquants exploitent la fonctionnalité de batching de GraphQL pour amplifier les attaques de façon exponentielle, pourquoi autoriser des entrées sous forme de tableau peut devenir un cauchemar d’épuisement des ressources, et surtout, comment protéger votre API GraphQL contre ces attaques dévastatrices.
Comprendre le batching dans GraphQL : une épée à double tranchant
Qu’est-ce que le batching dans GraphQL ?
Le batching dans GraphQL est une fonctionnalité documentée décrite dans la spécification GraphQL publiée en juin 2018 dans la section 6.3.1, qui permet d’envoyer plusieurs requêtes avec une seule requête GraphQL. Cette technique a été conçue pour résoudre un problème courant : réduire la surcharge réseau en consolidant plusieurs demandes de données en un seul appel HTTP.
Une seule batch transite sur le réseau, puis toutes les requêtes sont exécutées séquentiellement. Bien que cela améliore la performance pour les utilisateurs légitimes, cela crée une surface d’attaque massive pour les acteurs malveillants.
Deux types d’attaques par batching
GraphQL supporte deux méthodes principales de batching, chacune avec ses implications en matière de sécurité :
1. Batching basé sur les tableaux
Le batching basé sur les tableaux permet aux attaquants d’envoyer plusieurs opérations ensemble dans une seule requête HTTP, mais il présente un inconvénient majeur : les tableaux ne sont pas disponibles partout. Lorsqu’ils sont supportés, les attaquants peuvent structurer leurs requêtes comme ceci :
[
{"query": "mutation { login(username: \"user1\", password: \"pass1\") { token } }"},
{"query": "mutation { login(username: \"user2\", password: \"pass2\") { token } }"},
{"query": "mutation { login(username: \"user3\", password: \"pass3\") { token } }"}
]
2. Batching basé sur les alias
Les alias sont très utiles car ils font partie de la spécification GraphQL, comme indiqué dans la section 2.5. Lorsqu’on interroge une API GraphQL, on doit s’attendre à avoir des alias disponibles. Cependant, les alias ont un inconvénient : ils ne peuvent être appliqués qu’aux requêtes ou aux mutations, mais pas aux deux en même temps dans une seule requête HTTP.
Les attaquants peuvent exploiter les alias pour réaliser une forme de batching où une seule requête contient des dizaines ou même des centaines de requêtes aliasées qui s’exécutent séquentiellement.
La mathématique de l’amplification des attaques
De 1 requête à 10 000 appels à la base de données
La nature exponentielle des attaques par batching les rend si dangereuses. Considérons ce scénario :
Attaque traditionnelle (sans batching) : - 1 requête HTTP = 1 requête à la base de données - Pour tester 10 000 mots de passe = 10 000 requêtes HTTP - Facilement détecté par les limiteurs de débit et WAFs
Attaque par batching : - 1 requête HTTP = 100 à 1 000 requêtes à la base de données - Pour tester 10 000 mots de passe = 10 à 100 requêtes HTTP - Complètement invisible pour les outils de sécurité traditionnels
C’est une méthode simple pour contourner les limites de tentatives et de débit. En effet, ces limites sont généralement fixées sur le nombre d’appels API, mais que faire si un seul appel API peut générer 10 000 requêtes à la base ? Les outils de sécurité comme les pare-feux et les limiteurs de débit ne peuvent pas détecter les activités anormales : ils ne surveillent que les appels API.
Impact réel : explosion de la complexité
Avec un seul appel réseau se traduisant par de nombreuses requêtes ou demandes d’objets, la communication de l’application avec la base de données peut entraîner une surcharge CPU ou mémoire, rendant potentiellement le service inaccessible aux utilisateurs légitimes.
Le problème devient encore plus grave avec des requêtes imbriquées. Imaginez une plateforme de médias sociaux où les utilisateurs appartiennent à des groupes, et les groupes contiennent des utilisateurs. Un attaquant pourrait élaborer une requête comme :
query {
group(id: "123") {
users(limit: 100) {
groups(limit: 100) {
users(limit: 100) {
groups(limit: 100) {
name
}
}
}
}
}
}
En seulement quatre niveaux d’imbrication avec une limite de 100 à chaque niveau, cette requête pourrait tenter de récupérer 100 × 100 × 100 × 100 = 100 millions de résultats, provoquant une surcharge catastrophique de la base.
Vecteurs d’attaque : comment les attaques par batching se manifestent
1. Contournement brut de l’authentification
En combinant plusieurs opérations dans une seule requête, les attaquants peuvent organiser des attaques par batching et tenter de contourner les mesures de sécurité telles que la limitation de débit.
Un attaquant pourrait tenter une attaque par force brute, via la fonctionnalité de batching de GraphQL, pour envoyer plusieurs requêtes dans une seule requête HTTP, afin de deviner le mot de passe, et en recevant un token, leur donner un accès complet à l’API en tant qu’utilisateur authentifié.
Exemple d’attaque :
[
{"query": "mutation { login(email: \"victim@example.com\", password: \"password123\") }"},
{"query": "mutation { login(email: \"victim@example.com\", password: \"123456\") }"},
{"query": "mutation { login(email: \"victim@example.com\", password: \"qwerty\") }"},
// ... 997 autres tentatives
]
Une seule requête contenant 1 000 tentatives de connexion apparaît comme une seule requête HTTP pour les systèmes de surveillance, évitant complètement la détection par limiteurs de débit.
2. Contournement de l’authentification à deux facteurs (2FA)
Peut-être la vulnérabilité la plus alarmante est la capacité de contourner l’authentification à deux facteurs. En utilisant l’attaque par batching de GraphQL, il est possible de contourner complètement l’un des facteurs d’authentification secondaires courants, le OTP (mot de passe à usage unique), en envoyant toutes les variantes de jetons dans une seule requête.
Étant donné que les codes OTP ont généralement seulement 1 000 000 de combinaisons possibles (pour des codes à 6 chiffres), un attaquant peut : 1. Déclencher la génération de OTP 2. Envoyer une requête batch avec tous les codes possibles 3. Traiter toutes les tentatives simultanément 4. Se connecter avec succès lorsque le bon code est trouvé
L’application web GraphQL vulnérable traitait tous les “jetons à usage unique” en même temps, en trouvant un valide, et en connectant l’attaquant.
3. Attaques par déni de service (DoS)
GraphQL supporte le batching de requêtes, ce qui consiste à envoyer une série de requêtes en une seule fois. Cela signifie qu’une seule requête HTTP contient plusieurs requêtes GraphQL, tandis que le traitement en backend est effectué l’une après l’autre. Ce traitement peut entraîner un déni de service au niveau de l’application, puisqu’un seul appel réseau se traduit par un grand nombre de requêtes ou demandes d’objets.
Les insights issus de 13 000 problèmes d’API GraphQL révèlent que 80 % des problèmes auraient pu être résolus en appliquant des bonnes pratiques telles que le contrôle d’accès avec autorisation et authentification, la validation des entrées, et la limitation de débit pour bloquer les attaques par force brute.
4. Enumeration et scraping de données
Les attaques par batching introduisent des vecteurs d’attaque supplémentaires, comme la force brute et l’énumération d’objets ciblant des informations utilisateur telles que noms, emails, et comptes.
Les attaquants peuvent efficacement énumérer : - Comptes utilisateur - Adresses email - Identifiants de ressources - Données privées via devinette d’ID séquentiels
Lors d’une récente opération, une API construite avec GraphQL permettait de récupérer des informations de santé du patient (PHI) lorsqu’un “numéro de séquence” valide était fourni dans une requête GraphQL. Ce numéro de séquence était généré aléatoirement par compte ; cependant, ce n’était pas un UUID long et pouvait facilement être énuméré.
Pourquoi les mesures de sécurité traditionnelles échouent
Le problème de limitation de débit
La limitation de débit traditionnelle fonctionne au niveau des requêtes HTTP, comptant le nombre d’appels API par période. Cependant :
Cette approche induirait en erreur les applications de surveillance du débit externes en leur faisant croire que tout va bien et qu’il n’y a pas de bot tentant de deviner les mots de passe.
Vue de sécurité traditionnelle :
Client IP : 192.168.1.100
Requêtes par minute : 5
Statut : ✓ Trafic normal
Réalité :
Client IP : 192.168.1.100
Requêtes HTTP : 5
Opérations réelles : 5 000
Requêtes à la base : 50 000
Statut : ✗ Attaque active
L’angle mort du WAF et du pare-feu
Pour les outils responsables de la sécurisation des applications web, comme les WAFs et les RASP, il est difficile d’identifier une activité serveur anormale lorsque chaque requête API peut encapsuler des milliers de requêtes malveillantes composant une attaque.
Les Web Application Firewalls (WAFs) modernes analysent les modèles de trafic HTTP, mais ils ne peuvent généralement pas analyser en profondeur la sémantique des requêtes GraphQL pour comprendre le coût computationnel réel de chaque requête.
La lacune de surveillance
Par conséquent, tous vos mécanismes de protection habituels échoueront à détecter et à refuser une attaque par force brute. Ainsi, un attaquant peut simplement faire un seul appel API avec des centaines de tentatives pour trouver des identifiants utilisateur (email, mots de passe). Ils peuvent également contourner l’authentification à deux facteurs (2FA) en envoyant toutes les variantes de jetons du mot de passe à usage unique (OTP).
Stratégies de défense complètes
1. Mettre en œuvre une analyse de la complexité des requêtes
La complexité correspond au nombre de champs dans la requête. La complexité par défaut de chaque champ est 1. Cependant, vous devriez attribuer des valeurs de complexité personnalisées en fonction du coût computationnel :
const schema = Schema.build(Query, EmptyMutation, EmptySubscription)
.limit_complexity(100) // Complexité maximale autorisée
.finish();
L’analyse de la complexité des requêtes vous permet de définir la difficulté de chaque champ, et de restreindre les requêtes à une complexité maximale. L’idée est de définir la difficulté de chaque champ en utilisant un nombre simple.
Exemple d’implémentation :
field :posts, [PostType], null: false do
argument :limit, Integer, required: false, default_value: 10
complexity -; (ctx, args, child_complexity) {
args[:limit] * child_complexity
}
end
2. Imposer une limitation de la profondeur des requêtes
En limitant la profondeur des requêtes, nous pouvons empêcher l’exécution de requêtes excessivement complexes et gourmandes en ressources.
Cette bibliothèque valide la profondeur totale des requêtes et mutations. Une limite de profondeur de requête générera une erreur de validation pour les requêtes ou mutations dépassant cette limite.
Exemple d’implémentation :
import depthLimit from 'graphql-depth-limit';
app.use('/graphql', graphqlHTTP({
schema,
validationRules: [depthLimit(10)]
}));
Prenons l’exemple d’un serveur configuré avec une profondeur maximale de requête de 3, toute requête dépassant cette profondeur serait considérée comme invalide.
3. Restreindre les limites d’opérations batch
Limiter le nombre d’opérations pouvant être regroupées et exécutées en une seule fois est une autre option pour atténuer les attaques par batching GraphQL menant à un DoS. Ce n’est pas une solution miracle, mais cela doit être utilisé en complément d’autres méthodes.
Bonnes pratiques : - Limiter le batching basé sur les tableaux à 5-10 opérations maximum - Restreindre le nombre d’alias par requête (par exemple, maximum 20 alias) - Imposer des limites de taille du corps des requêtes HTTP
4. Désactiver le batching pour les opérations sensibles
Une autre option consiste à empêcher le batching pour les objets sensibles que vous ne souhaitez pas voir soumis à une attaque par force brute, comme les noms d’utilisateur, emails, mots de passe, OTP, jetons de session, etc. Ainsi, un attaquant est obligé d’attaquer l’API comme une API REST et de faire un appel réseau différent par instance d’objet.
Opérations critiques à protéger : - Mutations d’authentification - Opérations de réinitialisation de mot de passe - Vérification OTP - Traitement des paiements - Modification de compte
5. Mettre en œuvre une limitation de débit au niveau des opérations
Passer d’une limitation de débit au niveau HTTP à une limitation par opération :
const rateLimits = {
login: { max: 5, window: '1m' },
verifyOTP: { max: 3, window: '5m' },
resetPassword: { max: 3, window: '1h' }
};
Cela garantit que même si les opérations sont regroupées, le décompte cumulatif déclenche les limites de débit.
6. Mettre en place des mécanismes de timeout pour les requêtes
Il existe une autre approche consistant à limiter le temps d’exécution des requêtes : le “timeout de sécurité”. Il ne s’agit plus de bloquer les requêtes trop exigeantes avant leur exécution, mais de surveiller leur temps d’exécution et de les arrêter si elles prennent trop longtemps.
Définissez des délais d’attente raisonnables à plusieurs niveaux : - Niveau du résolveur GraphQL : 1-2 secondes par résolveur - Niveau d’exécution de la requête : 5-10 secondes au total - Niveau de la requête HTTP : 30 secondes maximum
7. Utiliser des outils de sécurité GraphQL
Chez Escape, nous avons créé GraphQL Armor, un plugin open-source pour les implémentations JavaScript de GraphQL, qui applique par défaut les meilleures pratiques de sécurité à votre API.
Outils de sécurité recommandés : - GraphQL Armor : plugin de sécurité complet - graphql-depth-limit : bibliothèque de limitation de profondeur - graphql-query-complexity : analyse de la complexité - Escape Security Scanner : détection automatisée des vulnérabilités
8. Mettre en œuvre une validation correcte des entrées
80 % des problèmes auraient pu être résolus en appliquant les meilleures pratiques telles que le contrôle d’accès avec autorisation et authentification, la validation des entrées, et la limitation de débit pour bloquer les attaques par force brute.
Liste de vérification de validation : - Valider les plages d’arguments (par ex., limit : 1-100) - Sanitize toutes les entrées utilisateur - Imposer des contraintes de type de données - Rejeter les chaînes trop longues - Limiter la taille des tableaux
9. Surveiller et enregistrer les modèles de requêtes
Mettre en place une journalisation complète pour détecter les modèles d’attaque :
class LogQueryComplexity {
result() {
const complexity = super();
logger.info(`[GraphQL] Complexité : ${complexity}, Profondeur : ${depth}, Opérations : ${operations}`);
}
}
Alerter sur les modèles suspects : - Pics soudains de la complexité des requêtes - Tentatives d’authentification échouées répétées - Modèles d batching inhabituels - Requêtes provenant d’acteurs malveillants connus
10. Utiliser des requêtes persistantes en production
Il est fortement recommandé de n’accepter que des documents de confiance sur votre serveur GraphQL — comme Facebook le fait en interne.
Mettre en place une liste blanche des requêtes autorisées : - Enregistrer préalablement toutes les requêtes légitimes - Rejeter toute requête ad hoc en production - Utiliser des identifiants de requête au lieu de chaînes complètes - Maintenir un contrôle strict des versions
Bonnes pratiques de configuration de sécurité
Paramètres par défaut sécurisés
Par défaut, la plupart des implémentations GraphQL ont des configurations par défaut peu sécurisées qu’il faut modifier : ne pas renvoyer des messages d’erreur excessifs.
Liste de vérification de configuration en production :
const secureConfig = {
// Complexité et profondeur
maxComplexity: 100,
maxDepth: 7,
maxListDepth: 3,
// Limites de batching
maxBatchSize: 10,
maxAliases: 15,
// Délais d'attente
queryTimeout: 5000,
resolverTimeout: 1000,
// Fonctionnalités de sécurité
introspection: false, // Désactiver en production
playground: false, // Désactiver en production
debug: false, // Masquer les traces de pile
// Limitation de débit
rateLimit: {
window: '15m',
max: 100,
skipSuccessfulRequests: false
}
};
Gestion des erreurs
Les API GraphQL en production ne doivent pas renvoyer de traces de pile détaillées ou être en mode débogage. Les propriétaires de GraphQL doivent utiliser un middleware pour contrôler les erreurs renvoyées par le serveur. De plus, ils doivent masquer les erreurs et les rendre disponibles aux développeurs mais pas aux appelants de l’API.
Renvoyer des messages d’erreur génériques aux clients :
{
"errors": [{
"message": "Une erreur est survenue lors du traitement de votre requête",
"extensions": {
"code": "INTERNAL_SERVER_ERROR"
}
}]
}
Enregistrer les erreurs détaillées côté serveur pour le débogage.
Tester la sécurité de votre API GraphQL
Liste de vérification des tests de sécurité
Tests d’attaque par batching
- Envoyer plus de 100 opérations en une seule requête
- Vérifier les limites du batching basé sur les tableaux
- Vérifier les restrictions d’alias
Tests d’authentification
- Tenter de contourner OTP avec des codes batchés
- Tester la force brute de mot de passe via batching
- Vérifier la protection contre l’authentification multifactorielle
Simulation de DoS
- Envoyer des requêtes profondément imbriquées
- Tester des requêtes avec des scores de complexité élevés
- Vérifier l’activation des mécanismes de timeout
Tests d’énumération
- Tenter une énumération d’ID utilisateur
- Tester la validation des adresses email
- Vérifier les contrôles d’accès aux ressources
Analyse de sécurité automatisée
Chez Escape, nous avons créé un scanner spécialisé dans les API GraphQL. Il peut simplifier ce processus en identifiant rapidement les problèmes sur vos points de terminaison API et en fournissant des extraits de remédiation — en seulement 15 minutes, sans intégrations complexes ni surveillance du trafic.
Utilisez régulièrement des outils automatisés : - Scans de sécurité hebdomadaires - Tests avant déploiement - Surveillance continue en production - Tests de pénétration réguliers
Études de cas réelles
Étude de cas 1 : Fuite d’API de santé
Lors d’une opération récente, une API construite avec GraphQL permettait de récupérer des informations de santé du patient (PHI) lorsqu’un “numéro de séquence” valide était fourni. Ce numéro était généré aléatoirement par compte ; cependant, ce n’était pas un UUID long et pouvait facilement être énuméré.
Méthode d’attaque : - Numéros de séquence à 9 chiffres (10^9 possibilités) - Le batching a réduit l’attaque d’1 milliard de requêtes à 100 000 - Utilisation de Turbo Intruder avec batching optimisé - Énumération réussie des dossiers patients
Leçon : Ne jamais se fier uniquement aux identifiants “difficiles à deviner”. Mettre en œuvre une authentification correcte et une limitation de débit au niveau des opérations.
Étude de cas 2 : Plateforme e-commerce
Cela se résout généralement par une technique de batching, où plusieurs demandes de données provenant d’un backend sont collectées sur une courte période, puis dispatchées dans une seule requête à une base de données ou microservice sous-jacent.
Une plateforme e-commerce a connu des prises de contrôle de comptes via des attaques par force brute par batching : - Les attaquants ont envoyé 1 000 tentatives de mot de passe par requête HTTP - La limitation de débit a manqué l’attaque (seulement 10 requêtes HTTP au total) - Plus de 500 comptes compromis avant détection - Résultat : plus de 2 millions de dollars en transactions frauduleuses
Mesure d’atténuation : Mise en œuvre d’une limitation de débit au niveau des opérations et désactivation du batching pour les mutations d’authentification.
L’avenir de la sécurité GraphQL
Normes émergentes et meilleures pratiques
La communauté GraphQL travaille activement à l’amélioration de la sécurité :
Directives de sécurité standardisées
- Déclarations de sécurité au niveau du schéma
- Support intégré de limitation de débit
- Analyse native de la complexité
Outils améliorés
- Meilleurs outils d’analyse statique
- Tests de sécurité automatisés
- Détection des menaces en temps réel
Éducation communautaire
- Cours de sécurité axés sur GraphQL
- Bibliothèques de sécurité open-source
- Bases de données de vulnérabilités partagées
Conclusion : Équilibrer puissance et sécurité
Les attaques par batching dans GraphQL représentent un défi fondamental dans la sécurité des API modernes : comment maintenir la flexibilité et la puissance qui rendent GraphQL attrayant tout en se protégeant contre l’amplification exponentielle des attaques ?
Il existe une nouvelle surface d’attaque lorsque la pile technologique de l’application inclut GraphQL. La première ligne de défense est une programmation sécurisée. Il faut faire attention en suivant la spécification et prêter attention lors de l’implémentation de méthodes spécifiques qui pourraient nécessiter une filtration des données et des limites de débit.
Points clés à retenir :
- Comprendre le risque : une seule requête GraphQL peut déclencher des milliers d’opérations sur la base
- Superposer les défenses : aucune mitigation unique n’est suffisante ; utilisez plusieurs stratégies
- Surveiller en continu : suivre la complexité, la profondeur, et le nombre d’opérations
- Tester régulièrement : l’analyse de sécurité automatisée doit faire partie de votre pipeline CI/CD
- Rester à jour : la sécurité GraphQL évolue ; suivez les meilleures pratiques
En mettant en œuvre les stratégies de défense complètes décrites dans ce guide, vous pouvez exploiter les fonctionnalités puissantes de GraphQL tout en protégeant votre infrastructure contre les attaques par batching et l’épuisement des ressources. Rappelez-vous : la sécurité n’est pas une configuration ponctuelle, mais un processus continu de surveillance, de test et d’amélioration.
La commodité du batching dans GraphQL ne doit pas se faire au détriment de la sécurité — avec des sauvegardes appropriées, vous pouvez avoir à la fois performance et protection.
Ressources supplémentaires
- OWASP GraphQL Cheat Sheet
- GraphQL Armor Security Plugin
- Rapport sur la sécurité de GraphQL 2024
- Bibliothèque de limitation de profondeur GraphQL
Mots-clés : sécurité GraphQL, attaques par batching, sécurité API, complexité des requêtes, prévention DoS, vulnérabilités GraphQL, limitation de débit, contournement d’authentification, sécurité 2FA, bonnes pratiques GraphQL
Related InstaTunnel pages
Continue from this article into the most relevant product guides and workflows.
Related Topics
Keep building with InstaTunnel
Read the docs for implementation details or compare plans before you ship.