GraphQL est un langage de requête API puissant qui a gagné en popularité ces dernières années pour sa flexibilité et sa capacité à offrir une excellente expérience aux développeurs. Cependant, cette adoption croissante s’accompagne de potentielles vulnérabilités de sécurité et menaces de sécurité.
Dans cet article, nous expliquerons ce qu’est GraphQL, dans quels cas il peut être un meilleur choix qu’une API REST, avec des exemples des différentes attaques que vous pourriez subir sur votre application GraphQL, et enfin, nous proposerons quelques conseils qui aideront à renforcer votre posture de sécurité.
1. Qu’est-ce que GraphQL ?
Développé en 2012 et rendu open-source en 2015 par Meta, GraphQL (Graph Query Language) est un langage de requête pour les API. Depuis 2019, son développement est supervisé par la “GraphQL Foundation”. La principale fonctionnalité de GraphQL est de permettre aux clients de demander et de recevoir précisément les données dont ils ont besoin, et rien de plus.
Cette technologie a été pensée pour répondre aux limites bien connues des API REpresentational State Transfer (REST), notamment la over-fetching ou la under-fetching de données. L’ over-fetching se produit lorsqu’une requête renvoie davantage d’informations que nécessaire. L’under-fetching correspond à la situation inverse, où une requête ne fournit qu’une partie des données attendues.
Contrairement aux APIs REST, qui imposent généralement un format de réponse prédéfini et rigide, GraphQL offre une flexibilité aux clients de spécifier les champs et les relations souhaités. Cette approche permet une récupération de données plus efficace et améliore considérablement l’expérience de développement.
Prenons un cas concret pour bien comprendre son usage: considérons une API qui permet aux clients d’interagir avec une collection de livres. Nous voulons l’interroger pour obtenir uniquement la liste des titres et de leurs auteurs.
Cette requête GraphQL ci-dessous renverra une liste de tous les livres, incluant le titre de chaque ouvrage et son auteur. Le champ auteur est un objet imbriqué qui contient les champs firstName et lastName.

Figure 1 : Exemple d’une requête GraphQL
GraphQL vs REST
Plusieurs raisons incitent les développeurs à privilégier GraphQL plutôt que REST pour leurs API. L’une des raisons les plus importantes est l’efficacité qu’il offre lors de la récupération des données.
Avec REST, le serveur met à disposition un ensemble de différents endpoints. Le client doit alors effectuer plusieurs requêtes vers ces différents points d’accès pour obtenir les données nécessaires à l’affichage d’une seule page. Cette approche peut entraîner une expérience utilisateur lente et inefficace.
En revanche, GraphQL ne propose qu’un point d’accès unique. Les clients peuvent l’utiliser pour demander précisément les données spécifiques dont ils ont besoin. De cette manière, la communication entre le client et le serveur devient bien plus efficace, car le client ne reçoit que les données requises en une seule et unique requête.
Considérons que notre application dispose d’une vue qui affiche les détails d’un utilisateur dont son firstName, lastname et occupation. Avec une API REST, vous vous dirigeriez sûrement vers le point d’accès /users/<id> pour récupérer les informations liées à l’utilisateur.
Ainsi, la réponse obtenue pourrait correspondre à celle ci-dessous:

Figure 2 : Réponse de l’API REST
Dans ce cas précis, vous avez récupéré plus de données que nécessaire. Une API GraphQL ne nécessiterait que les trois champs souhaités :

Figure 3 : Requête GraphQL
Analyse Comparative des Architectures d’API REST et GraphQL
| Caractéristique | REST | GraphQL |
| Approche | Orienté ressources, multiples URL | Orienté données, schéma unique |
| Modèle d’interaction | Le client interagit avec plusieurs endpoints aux réponses fixes. | Le client envoie des requêtes ciblées via un endpoint unique. |
| Données retournées | Fixes (définies par le serveur) | Flexibles (définies par le client) |
| Défi principal | Sur/sous-collecte des données | Complexité des requêtes |
| Versioning | Souvent nécessaire (ex: /v1, /v2) | Moins fréquent (évolution du schéma) |
| Cache | Simple (HTTP natif) | Complexe (outils dédiés) |
| Documentation | Externe (ex: Swagger) | Intégrée et interactive |
La structure et le fonctionnement de GraphQL
Avant d’explorer les vulnérabilités spécifiques aux API GraphQL, une compréhension approfondie de leur fonctionnement est essentielle. Nous allons donc détailler leur structure et leurs principaux concepts.

Figure 4: Schéma GraphQL
Schéma GraphQL
Le schéma est la pierre angulaire de toutes API GraphQL. Il représente la structure fondamentale régissant toutes les interactions possibles entre le serveur et les clients. Pour une API GraphQL donnée, il n’existe qu’un seul schéma, qui sert de référence unique.
Rédigé dans un langage de définition de schéma (SDL – Schema Definition Language) clair et lisible, le schéma définit précisément :
- Les types de requêtes et d’opérations (mutations, souscriptions) que les clients sont autorisés à soumettre.
- La structure des données que le serveur peut exposer et que les clients peuvent récupérer.
- Les relations intrinsèques entre ces différents types de données.
Le schéma expose les données disponibles sous forme d’une série de types, qui seront ensuite implémentés côté serveur.
Le type objet
Les types d’objets sont les éléments les plus courants et représentent les entités que votre API manipule. Chaque type d’objet possède un nom unique. Par exemple, les types d’objets Utilisateur, Livre, Produit sont définis par un ensemble de champs.
- Champs (Fields) : Ce sont les attributs ou propriétés d’un type d’objet. Chaque champ a un nom (ex: prénom, type, prix) et un type de retour qui spécifie la nature de la donnée attendue. Ce type de retour peut être un autre type de Object, un type Énumération, un type Union, un type Interface.
- Arguments : Les champs peuvent accepter des arguments pour modifier ou filtrer la donnée qu’ils retournent. Par exemple, un champ utilisateur pourrait prendre un argument id (utilisateur(id: « 123 »). Ces arguments sont fortement typés et définis dans le schéma.
- Alias : Il est possible de renommer un champ dans la réponse de la requête en utilisant un alias. Cela est particulièrement utile lorsque vous avez besoin de récupérer le même champ plusieurs fois avec des arguments différents dans une seule requête.
Le type scalaire
Le type scalaire est le type de données le plus élémentaire dans GraphQL. GraphQL propose un ensemble de scalaires intégrés :
- Int : un nombre entier
- Float : un nombre à virgule
- String : une chaîne de caractère
- Boolean : true ou false
- ID : un identifiant unique
L’exemple suivant illustre l’utilisation de types scalaires pour définir l’objet devoteamArticles :

Figure 5: Type scalaire
Les opérations
Requêtes (Queries) : opérations utilisées pour lire ou récupérer des données depuis le serveur. Elles constituent l’équivalent fonctionnel des requêtes GET dans le contexte des API REST.

Figure 6: Requête
Mutations : opérations utilisées pour modifier l’état des données sur le serveur. Elles englobent les actions de création, mise à jour et suppression de données, ce qui correspond aux méthodes POST, PUT, PATCH et DELETE des API REST.

Figure 7: Mutation
Souscriptions : opérations qui permettent aux clients de recevoir des mises à jour en temps réel du serveur. Plutôt que de faire une requête ponctuelle ou une mutation, le client « s’abonne » à un événement et le serveur pousse des données vers le client chaque fois que cet événement se produit.
Vulnérabilités et attaques sur les API GraphQL
La flexibilité de GraphQL, son principal atout, constitue également sa principale surface d’attaque. Des requêtes client malveillantes, trop profondes ou complexes, peuvent surcharger les serveurs jusqu’au déni de service. Au-delà de ces vecteurs spécifiques, une API GraphQL demeure une application web classique et reste donc exposée aux menaces traditionnelles comme les injections, si les fondamentaux de la sécurité ne sont pas appliqués.
Cette section présente les principales vulnérabilités et attaques ciblant les API GraphQL.
Introspection attack
Par défaut, la majorité des instances GraphQL activent le système d’introspection. Ce mécanisme expose le schéma de l’API de manière programmatique, permettant aux outils de l’écosystème de découvrir automatiquement ses types, ses requêtes et ses mutations. Cette découverte dynamique facilite le développement et le test GraphQL. L’introspection s’avère particulièrement utile dans les environnements complexes, où les API évoluent rapidement et où maintenir une documentation manuelle à jour serait fastidieux.
L’introspection n’est pas vraiment une vulnérabilité, mais elle peut être utilisée et détournée par des attaquants cherchant à obtenir des informations sensibles sur une implémentation GraphQL. Un attaquant peut exploiter le mécanisme d’introspection pour obtenir une cartographie complète du schéma de l’API. Cette requête malveillante expose des informations détaillées sur :
- Les types de données disponibles
- Les champs et leurs relations
- Les mutations (opérations de modification des données) supportées
- Les arguments requis pour chaque requête
Cette connaissance approfondie lui permet de formuler des attaques plus ciblées, telles que :
- Le contournement des contrôles d’accès en identifiant des champs cachés ou des relations non documentées.
- L’exfiltration de données en construisant des requêtes complexes pour accéder à des informations sensibles.
- Le lancement d’attaques par déni de service en identifiant des requêtes potentiellement coûteuses.
Voici un exemple de ce à quoi cette requête pourrait ressembler :

Figure 8: introspection GraphQL
L’exemple illustre une requête d’introspection GraphQL qui expose la structure du schéma, y compris les types et les mutations disponibles. À partir de cette introspection, un attaquant pourrait identifier des champs sensibles, comme les identifiants et mots de passedes utilisateurs, ainsi que des opérations critiques telles que le login ou la modification de mot de passe, qu’il pourrait potentiellement exploiter.
IDOR (Insecure Direct Object Reference)
Les vulnérabilités IDOR (Insecure Direct Object Reference) se produisent lorsque l’API GraphQL expose des identifiants d’objets (comme des ID d’utilisateur, des ID de document, etc.) et ne parvient pas à vérifier correctement les autorisations de l’utilisateur pour accéder à ces objets. Un attaquant peut alors simplement modifier un paramètre dans sa requête pour accéder à des ressources auxquelles il ne devrait pas avoir droit.
Exemples:
- REST : Accès non autorisé via modification de l’identifiant dans l’URL (ex: /user/123 devient /user/456).
- GraphQL : Accès non autorisé via modification de l’identifiant dans des requêtes ou des mutations.
Comment cela fonctionne :
- Exposition des ID : L’API GraphQL retourne souvent des identifiants uniques pour les objets (par exemple, id: « 123 » pour un utilisateur, id: « 456 » pour une commande).
- Manque de contrôle d’accès : Lorsqu’un utilisateur authentifié envoie une requête pour accéder à un objet via son ID, le serveur ne vérifie pas systématiquement si cet utilisateur a les droits d’accès légitimes à cet objet.
- Exploitation : Un attaquant peut simplement changer l’ID de la ressource dans sa requête pour essayer d’accéder aux données d’un autre utilisateur ou à des ressources privilégiées.
Exemple concret :
Considérons une application dans laquelle une requête GraphQL getUser prend un identifiant (id) comme paramètre et retourne le nom ainsi que le mot de passe de l’utilisateur.

Figure 9: Requête avec IDOR
Si un attaquant connaît l’ID d’un autre utilisateur et que le serveur ne contrôle pas les autorisations, il peut accéder aux données sensibles du compte en modifiant le paramètre « id » de la requête getUser.
Conséquences :
- Accès non autorisé à des données sensibles (informations personnelles, historiques de commandes, documents confidentiels).
- Modification ou suppression de données appartenant à d’autres utilisateurs.
- Contournement des mécanismes de sécurité basés sur les autorisations.
Mutation Brute Force Attack
Les mutations GraphQL sont extrêmement puissantes : elles permettent de créer, modifier ou supprimer des données. Cependant, lorsqu’elles sont mal configurées ou insuffisamment protégées, elles peuvent être sujet à des attaques par bruteforce. Si aucune limite n’est imposée sur le nombre de tentatives, un attaquant peut automatiser l’envoi de nombreuses mutations pour tester différentes valeurs, comme des identifiants ou des mots de passe, jusqu’à trouver une combinaison valide.
GraphQL introduit en plus une particularité qui complique la détection : les alias. Cette fonctionnalité permet d’exécuter plusieurs mutations différentes dans une seule requête, réduisant ainsi le volume de trafic apparent côté serveur.
Résultat : un grand nombre de tentatives peut passer inaperçu, rendant les mécanismes de protection classiques, basés uniquement sur le comptage des requêtes, beaucoup moins efficaces.
Scénario d’attaque :
Considérons une mutation d’authentification simple décrite ci-dessous:

Figure 10: Mutation avec Alias de login
La Requête ci-dessus décrit plusieurs tentatives de connexion effectuées en parallèle. Chacune testant un mot de passe différent. Si l’un des alias retourne la valeur success: true et aussi un token valide alors l’attaquant a trouvé le bon mot de passe, sans générer une série de requêtes séparées qui pourraient déclencher un mécanisme de protection traditionnel basé sur le volume ou le nombre de tentatives.
Conséquences :
- Compromission de comptes utilisateurs par bruteforce des mots de passe.
- Contournement des protections basées uniquement sur la détection de volumes anormaux de requêtes.
- Impact sur les performances dû à un déni de service si l’attaque est menée de manière massivement parallèle.
CSRF over GraphQL
Le Cross-Site Request Forgery (CSRF) se produit lorsqu’un site malveillant pousse un utilisateur connecté à envoyer une requête non autorisée, sans qu’il s’en rende compte. Bien que souvent associées aux applications web traditionnelles, les API GraphQL peuvent également être vulnérables.
Comment ça marche : un attaquant élabore une requête malveillante (par exemple, une mutation GraphQL) et incite un utilisateur connecté à l’exécuter, souvent via un lien malveillant. Étant donné que l’utilisateur est authentifié, son navigateur envoie automatiquement ses cookies de session ce qui fait apparaître la requête comme légitime pour l’API GraphQL

Figure 11: CSRF [1]
Vulnérabilité : Les API GraphQL qui s’appuient uniquement sur l’authentification par cookies, sans mécanismes de protection CSRF supplémentaires, sont susceptibles d’être compromis. Dans une API GraphQL, si aucune protection CSRF n’est en place, et si l’API accepte certaines formes de requêtes (comme des GET ou des requêtes en x-www-form-urlencoded) alors elle peut devenir vulnérable.
Scénario d’attaque
Le cœur de cette attaque repose sur la capacité de l’API GraphQL à accepter des requêtes avec un type de contenu (Content-Type) différent de celui qu’elle utilise habituellement. Ce type de scénario décrit une faille d’implémentation courante.
1. Reconnaissance et découverte : L’attaquant identifie d’abord une fonctionnalité vulnérable, comme la mutation changeEmail, dans l’application web. Il observe que cette opération est normalement effectuée via une requête POST avec un Content-Type = application/json
Figure 12: Découverte
2. Test de vulnérabilité : Pour vérifier si l’API est vulnérable à une attaque de type CSRF, l’attaquant essaie d’envoyer la même requête de mutation en changeant le Content-Type à application/x-www-form-urlencoded. S’agissant d’un format standard pour les formulaires HTML, sa génération ne requiert aucun code JavaScript. Par conséquent, si l’API traite cette requête avec succès, la présence de la vulnérabilité est avérée.

Figure 13: Test de la faille
3. Création du code d’exploitation : L’attaquant utilise un outil similaire à Burp Suite Professional pour générer automatiquement une page HTML permettant d’établir un « Proof of Concept ». Ce code malveillant contient un formulaire HTML caché inclus dans la balise(<form>) dont l’action pointe vers le point de terminaison de l’API GraphQL.

Figure 14: Fonctionnalité Burp Suite Pro
- Préparation de la charge utile (Payload) : À l’intérieur du formulaire, l’attaquant insère des champs de saisie (<input type= »hidden »>) pour chaque paramètre de la requête GraphQL. Ces champs masqués permettent d’inclure la mutation et les variables nécessaires (par exemple, la nouvelle adresse e-mail) dans le corps de la requête.

Figure 15: Payload vulnérable
5. Déclenchement de l’attaque : La page HTML inclut un script JavaScript qui soumet automatiquement le formulaire dès qu’elle est chargée. L’attaquant envoie ensuite un lien menant vers cette page malveillante à la victime à travers un mail.
6. Exécution : Lorsque la victime, déjà connectée à l’application vulnérable, clique sur un lien, son navigateur exécute le script qui soumet automatiquement un formulaire. Le navigateur inclut les cookies de session de la victime. La requête est alors envoyée à l’API GraphQL et est traitée comme une requête légitime, ce qui a pour conséquence de modifier l’e-mail de la victime à son insu.
Remarque
Veuillez noter que les images utilisées pour illustrer ce scénario proviennent d’un lab de PortSwigger, à des fins éducatives.
Autres vulnérabilités des API GraphQL
Attaques par injection
- Injection SQL : Si une application web ne valide pas correctement les données des requêtes GraphQL avant de les utiliser pour construire une requête SQL, un attaquant peut insérer des commandes SQL malveillantes. Cela peut lui permettre d’accéder, de modifier ou de supprimer des informations de la base de données.
- Injection XSS (Cross-Site Scripting) : Une faille XSS se produit lorsque l’application ne neutralise pas les caractères spéciaux dans les données envoyées par l’utilisateur. Un attaquant peut injecter un script malveillant dans les données qui, une fois affichées par la victime, s’exécutera dans son navigateur pour voler des informations de session ou des cookies.
Attaque par déni de service (DoS)
Les API GraphQL sont particulièrement vulnérables aux attaques par déni de service car elles permettent aux clients de faire des requêtes très complexes. Une requête unique et bien conçue, demandant une quantité massive de données, peut surcharger le serveur et la base de données, rendant l’application inutilisable pour les autres utilisateurs. Pour se protéger, il est essentiel d’implémenter un contrôle de complexité des requêtes, qui limite la profondeur et le nombre de champs qui peuvent être demandés dans une seule requête.
Atténuation
La sécurisation d’une API GraphQL exige d’adopter une approche proactive qui couvre les vulnérabilités les plus courantes.
Introspection
La fonctionnalité d’introspection est un outil puissant, mais cependant elle peut exposer la structure complète de votre API.
La stratégie de remédiation dépend du type d’API définit :
- Pour une API privée : désactivez l’introspection en production. Elle est utile pendant le développement pour permettre aux outils et aux développeurs de découvrir le schéma d’API, mais une fois que l’application est déployée, sa désactivation empêche les attaquants de cartographier facilement l’ensemble de l’API.
- Pour une API publique : si l’introspection est nécessaire, il faut s’assurer que le schéma n’expose rien de sensible. Valider manuellement que le schéma ne contient aucun champ critique (comme des informations personnelles telles que les e-mails, etc.). De plus, il est recommandé de désactiver l’autocomplétion dans les IDE GraphQL pour les environnements de production afin de bloquer des outils automatisés comme Clairvoyance qui peuvent exploiter ces fonctionnalités pour la reconnaissance.
IDOR (Insecure Direct Object Reference)
Une faille IDOR survient lorsqu’une application permet l’accès à un objet (fichier, donnée, compte) en se basant uniquement sur l’identifiant fourni par l’utilisateur, sans vérification adéquate des droits.
Voici les points essentiels pour s’en protéger :
- Toujours valider les autorisations du côté back-end pour chaque requête et pour chaque objet demandé.
- Ne jamais faire confiance au front-end pour masquer, gérer les champs ou les mutations.
- Vérifier que l’ID est fourni par l’utilisateur correspondant à l’ID qui lui est attribué dans sa session d’authentification.
- Rendre l’énumération des ressources plus difficile en utilisant :
- Des identifiants non prévisibles, comme des UUID (exemple : f4b47cc1-0a1b-4b73-a1e5-2b3d68408d3c).
- Ou des identifiants hachés, plutôt que des ID séquentiels (1, 2, 3, …).
Mutation Brute Force Attack
Pour contrer les attaques Brute Force, la priorité est de maîtriser le flux des échanges. Cela commence par une limitation stricte du débit, en n’autorisant par exemple que dix requêtes par minute par utilisateur ou adresse IP. Si les échecs de connexion s’accumulent sur un identifiant précis, le système doit également être capable de bloquer temporairement l’accès pour couper court à l’attaque.
Par ailleurs, pour éviter de surcharger le serveur avec des demandes trop gourmandes (DoS), il est nécessaire de poser des bornes techniques :
- Restreindre le nombre d’alias et d’opérations exécutables en un seul appel.
- Imposer une taille maximale en octets pour chaque requête acceptée.
CSRF over GraphQL
Remédiation : Pour se protéger efficacement des attaques CSRF, il est recommandé de combiner plusieurs mesures de sécurité :
- Validez le Content-Type : Acceptez uniquement les requêtes POST qui sont encodées en JSON (Content-Type: application/json). Cela bloque l’exploitation des failles qui reposent sur des formulaires HTML (qui utilisent application/x-www-form-urlencoded par défaut).
- Utilisez les jetons CSRF (CSRF Token) : Pour une vraie protection, mettez en œuvre des jetons CSRF. Le serveur génère un jeton unique et aléatoire pour chaque session, l’inclut dans le corps de la page et attend de le voir dans une en-tête de la requête (comme X-CSRF-Token). Toute requête de mutation qui n’inclut pas ce jeton valide est rejetée.
- Configurez les cookies avec l’attribut SameSite : Si votre API utilise des cookies, définissez l’attribut SameSite sur Lax ou Strict. L’attribut SameSite=Lax garantit que les cookies ne sont pas envoyés lors de requêtes inter-sites (sauf pour la navigation de niveau supérieur), tandis que SameSite=Strict les bloque dans toutes les requêtes inter-sites, offrant une protection encore plus forte contre les attaques CSRF.
Conclusion
Les API GraphQL offrent une grande flexibilité et une puissance considérable pour la conception d’applications modernes. Cependant, cette richesse fonctionnelle s’accompagne de risques spécifiques, tels que l’exfiltration de données, les attaques par déni de service via des requêtes trop complexes ou encore l’absence de contrôles fins sur les autorisations.
La sécurité de ces API ne peut donc pas être négligée : elle doit reposer sur une combinaison de bonnes pratiques (validation des schémas, limitation de la profondeur des requêtes, authentification et autorisation strictes) et d’outils adaptés.
Plus ces mesures sont intégrées tôt dans le cycle de développement, plus elles sont efficaces.
C’est précisément l’approche du SecDevOps, qui vise à intégrer la sécurité de manière continue dans les processus de développement et de déploiement. En faisant de la sécurité une responsabilité partagée entre les équipes techniques, il devient possible de tirer pleinement profit des atouts de GraphQL tout en garantissant la résilience des systèmes.