Comment sécuriser vos API GraphQL contre les injections et fuites de données en 7 étapes

Marc Lefebvre
Marc Lefebvre
Pentester senior — WebGuard Agency
| ·14 min de lecture

TL;DR

  • Les API GraphQL exposent une surface d’attaque spécifique que les protections REST classiques ne couvrent pas : introspection, requêtes imbriquées, batching, injections via les résolveurs.
  • Ce guide détaille 7 étapes concrètes pour sécuriser vos API GraphQL, de la désactivation de l’introspection au monitoring en production.
  • Chaque étape inclut des exemples de code et des recommandations directement applicables sur Apollo Server, graphql-yoga et Hasura.
  • Ce guide est basé sur plus de 40 pentests API GraphQL réalisés par WebGuard Agency en 2024-2026.

GraphQL a révolutionné la manière dont les applications consomment les API. Sa flexibilité — un endpoint unique, des requêtes sur mesure, un schéma auto-documenté — est exactement ce qui le rend intrinsèquement plus difficile à sécuriser qu’une API REST classique. Sur les 40+ pentests API GraphQL que nous avons menés chez WebGuard Agency entre 2024 et 2026, 92 % présentaient au moins une vulnérabilité critique liée à une mauvaise configuration de sécurité GraphQL.

Ce guide pratique vous accompagne en 7 étapes pour identifier et corriger les vulnérabilités les plus courantes. Chaque étape est tirée de notre expérience terrain et illustrée par des exemples concrets. Pour un panorama plus large de la sécurité API, consultez également notre guide sécurité API REST et GraphQL et notre article sur les vulnérabilités GraphQL.

SURFACE D ATTAQUE API GRAPHQL Client Query / Mutation /graphql Endpoint unique POST JSON body 1. Introspection → schema leak 2. Nested queries → DoS 3. Batching → brute force auth 4. Injection via resolvers 5. IDOR via relations traversal 7 ETAPES DE DEFENSE EN PROFONDEUR Introspection | Depth limiting | Cost analysis | Auth resolvers | Input validation | Rate limiting | Monitoring

Étape 1 : Désactiver l’introspection en production

L’introspection est la fonctionnalité qui permet à un client GraphQL de découvrir automatiquement l’intégralité du schéma : types, champs, relations, mutations, arguments. En développement, c’est un outil priceless pour les développeurs. En production, c’est une feuille de route pour les attaquants.

Sur nos pentests, l’introspection est activée en production dans 67 % des cas. Un attaquant envoie une simple requête { __schema { types { name fields { name type { name } } } } } et obtient la cartographie complète de votre API : tous les types de données, tous les champs disponibles, toutes les mutations, tous les arguments attendus. Avec cette carte, il peut identifier les champs sensibles (email, password, ssn, internalNotes), découvrir des mutations cachées (adminDeleteUser, changeRole), et cartographier les relations entre types pour planifier des requêtes d’exfiltration.

Implémentation pratique : sur Apollo Server, ajoutez introspection: process.env.NODE_ENV !== 'production' dans la configuration. Sur graphql-yoga, utilisez le plugin useDisableIntrospection(). Sur Hasura, définissez la variable d’environnement HASURA_GRAPHQL_ENABLE_INTROSPECTION=false. Vérifiez la désactivation en testant avec un outil comme les outils pentest open-source que nous recommandons, notamment InQL pour Burp Suite ou graphw00f.

Étape 2 : Limiter la profondeur et la complexité des requêtes

L’une des attaques les plus destructrices contre une API GraphQL est la requête profondément imbriquée. Imaginez un schéma où un User a des posts, chaque Post a des comments, chaque Comment a un author (qui est un User), qui a des posts, etc. Un attaquant peut construire une requête récursive de 50 niveaux de profondeur qui génère des millions de requêtes SQL en cascade, saturant votre base de données en quelques secondes.

Trois mécanismes complémentaires doivent être mis en place :

  • Depth limiting : limiter la profondeur maximale des requêtes. Avec la librairie graphql-depth-limit, configurez une profondeur de 6 à 10 niveaux selon votre schéma. Au-delà, la requête est rejetée avec une erreur explicite.
  • Query cost analysis : assigner un coût à chaque champ et rejeter les requêtes dépassant un budget total. La librairie graphql-query-complexity permet de définir un maximumComplexity (typiquement entre 500 et 1000 points) et d’attribuer un multiplicateur aux champs qui retournent des listes.
  • Pagination obligatoire : ne jamais retourner de liste sans pagination. Imposez les arguments first/last avec un maximum (par exemple 100) et retournez des Connection types avec curseurs plutôt que des listes brutes.

Sur nos pentests, nous testons systématiquement les limites de profondeur en incrémentant progressivement la profondeur de requête jusqu’à obtenir un timeout serveur ou une erreur. 71 % des API GraphQL testées ne possèdent aucune limite de profondeur, ce qui les rend vulnérables à un déni de service applicatif en une seule requête.

Notre avis d'expert

« Le depth limiting seul ne suffit pas. Nous avons observé des cas où une requête de profondeur 4 seulement, mais demandant 200 champs en largeur sur des types avec des résolveurs coûteux, suffisait à faire tomber une API. La combinaison depth limit + query cost analysis + pagination est le triptyque minimal. »

Étape 3 : Implémenter l’authentification et l’autorisation au niveau des résolveurs

C’est l’erreur la plus grave et la plus fréquente que nous rencontrons en pentest : l’autorisation est gérée au niveau du endpoint ou du middleware, mais pas au niveau des résolveurs individuels. En REST, chaque route a ses propres contrôles d’accès. En GraphQL, un utilisateur authentifié peut potentiellement atteindre n’importe quel résolveur via la traversée de relations.

Exemple concret tiré de nos audits : un utilisateur standard envoie la requête { me { organization { users { email role salary } } } }. L’authentification vérifie que l’utilisateur est connecté, mais le résolveur organization.users ne vérifie pas que l’utilisateur a le droit de voir la liste complète des employés, incluant leurs salaires. Résultat : fuite de données massive par traversée de relations.

La règle d’or : chaque résolveur doit vérifier indépendamment les droits de l’utilisateur appelant. Utilisez un système de directives d’autorisation (@auth, @hasRole) appliqué directement dans le schéma, ou un middleware d’autorisation comme graphql-shield qui permet de définir des règles déclaratives par type et par champ. Pour les architectures Hasura, configurez les permissions par rôle au niveau de chaque table et relation.

Nous recommandons également d’implémenter le field-level authorization : certains champs au sein d’un même type peuvent avoir des niveaux d’accès différents. Le champ email d’un User est visible par tous, mais salary n’est visible que par les RH et l’utilisateur concerné. Ce niveau de granularité est indispensable pour les API exposant des données sensibles.

Étape 4 : Valider et assainir toutes les entrées utilisateur

Le système de types de GraphQL offre une fausse sensation de sécurité. Oui, GraphQL valide que l’argument id est bien un ID! et que name est bien un String. Mais cette validation de type ne protège pas contre les injections : un String peut contenir du SQL, du NoSQL, du LDAP, du XSS ou des caractères de contrôle.

Les points d’injection les plus courants dans les API GraphQL que nous auditons :

  • Arguments de filtrage : les résolveurs qui construisent des requêtes SQL dynamiques à partir d’arguments where, filter ou orderBy sont vulnérables aux injections SQL si les valeurs ne sont pas paramétrées.
  • Mutations avec entrées libres : les mutations acceptant du texte libre (commentaires, descriptions, messages) sans sanitisation permettent le stockage de payloads XSS qui seront reflétés dans le frontend.
  • Scalaires personnalisés : les scalaires custom comme JSON ou Any court-circuitent la validation de type et ouvrent la porte à des injections de structure.
  • Directives personnalisées : les directives de schéma non validées qui passent des arguments directement aux couches inférieures (base de données, API tiers) sont des vecteurs d’injection indirects.

Recommandations : utilisez des scalaires custom validés (par exemple EmailAddress, URL, DateTime du package graphql-scalars) plutôt que String générique. Paramétrez toujours les requêtes SQL/NoSQL dans les résolveurs (jamais de concaténation de chaînes). Sanitisez les entrées HTML avec une librairie comme DOMPurify ou sanitize-html avant stockage.

Besoin d’un pentest de votre API GraphQL ?

Notre équipe de pentesters certifiés audite vos API GraphQL selon la méthodologie OWASP API Security Top 10. Rapport détaillé avec preuves d’exploitation et recommandations de remédiation priorisées. Plus de 40 pentests API GraphQL réalisés en 2024-2026.

Demander un devis pentest API →

Étape 5 : Contrôler le batching et les requêtes multiples

GraphQL supporte nativement le query batching : envoyer un tableau de requêtes dans un seul appel HTTP. Cette fonctionnalité, prévue pour optimiser les performances, est détournée par les attaquants pour deux types d’attaques :

Brute force par batching : au lieu d’envoyer 10 000 requêtes d’authentification une par une (facilement détecté par le rate limiting HTTP), l’attaquant envoie un seul POST contenant 10 000 mutations login avec des mots de passe différents. Le rate limiter HTTP voit une seule requête. Sur les 40+ pentests API GraphQL que nous avons réalisés, 58 % étaient vulnérables au brute force par batching.

Exfiltration massive par alias : GraphQL permet d’utiliser des alias pour envoyer la même requête plusieurs fois dans un seul appel. Un attaquant peut exfiltrer les données de milliers d’utilisateurs en une seule requête en utilisant des alias : { u1: user(id: "1") { email } u2: user(id: "2") { email } ... u10000: user(id: "10000") { email } }.

Contre-mesures :

  • Limiter le nombre de requêtes par batch (par exemple, maximum 5 requêtes par appel HTTP).
  • Limiter le nombre d’alias par requête (la librairie graphql-armor offre cette fonctionnalité).
  • Implémenter un rate limiting au niveau GraphQL (par opération, pas seulement par requête HTTP). Des solutions comme graphql-rate-limit ou le rate limiting intégré d’Apollo Server permettent de limiter par type d’opération et par champ.
  • Pour les mutations critiques (authentification, changement de mot de passe, paiement), rejeter tout batching.

Étape 6 : Sécuriser les abonnements WebSocket

Les subscriptions GraphQL via WebSocket introduisent un canal de communication persistant souvent négligé dans les audits de sécurité. Les vulnérabilités spécifiques que nous rencontrons :

  • Authentification manquante sur la connexion WebSocket : le handshake WebSocket doit vérifier le token d’authentification. Sans cela, un utilisateur anonyme peut s’abonner à des flux de données sensibles.
  • Absence de ré-authentification : une connexion WebSocket ouverte peut rester active après l’expiration du token JWT. Implémentez un mécanisme de vérification périodique (ping/pong avec validation token).
  • Fuite de données par subscription : un utilisateur s’abonne à orderUpdated et reçoit les mises à jour de toutes les commandes, pas seulement les siennes. Le filtre d’autorisation doit être appliqué à chaque message publié, pas seulement au moment de l’abonnement.
  • Déni de service par subscriptions multiples : sans limite, un attaquant peut ouvrir des milliers d’abonnements simultanés, saturant la mémoire serveur. Limitez le nombre d’abonnements par connexion et par utilisateur.

Pour les implémentations utilisant le protocole graphql-ws, configurez le hook onConnect pour valider le token et le hook onSubscribe pour vérifier les autorisations spécifiques à chaque abonnement. Pour les implémentations utilisant subscriptions-transport-ws (déprécié), migrez vers graphql-ws qui offre un modèle de sécurité plus robuste.

Notre avis d'expert

« Les subscriptions WebSocket sont le parent pauvre de la sécurité GraphQL. Sur nos 15 derniers pentests d’API avec subscriptions, 13 présentaient au moins une vulnérabilité critique sur le canal WebSocket. Le plus courant : aucune vérification d’autorisation sur les messages publiés. Le développeur pense que l’abonnement est filtré parce que la subscription porte le nom myOrderUpdated, mais côté serveur, le resolver publie tous les événements à tous les abonnés. »

DEFENSE EN PROFONDEUR API GRAPHQL — 7 COUCHES Couche 7 : Monitoring et alerting production Couche 6 : WebSocket auth + re-auth Couche 5 : Batching + alias limiting Couche 4 : Input validation + sanitisation Couche 3 : Resolver-level authorization Couche 2 : Depth + cost limiting Couche 1 : Introspection desactivee Chaque couche protege contre un vecteur d attaque specifique. Les 7 sont necessaires.

Étape 7 : Monitorer et auditer en continu

La sécurisation d’une API GraphQL ne s’arrête pas au déploiement des protections. Un monitoring spécifique GraphQL est essentiel pour détecter les tentatives d’attaque et les régressions de sécurité. Voici les métriques et alertes à mettre en place :

  • Logger toutes les requêtes rejetées (introspection bloquée, depth limit dépassé, cost limit dépassé, batching limit) avec le contexte utilisateur et l’IP source. Un pic de rejets sur un même endpoint indique une tentative d’attaque active.
  • Tracker les erreurs d’autorisation au niveau des résolveurs. Un utilisateur qui déclenche régulièrement des erreurs 403 sur des champs auxquels il n’a pas accès est probablement en train de tester les limites de l’API.
  • Surveiller les requêtes lentes : une requête GraphQL dont le temps d’exécution dépasse 5 secondes peut indiquer une tentative de déni de service ou une injection qui provoque un full table scan.
  • Auditer les changements de schéma : intégrer un diff de schéma dans votre pipeline CI/CD pour détecter l’ajout accidentel de champs sensibles ou la suppression de directives d’autorisation.
  • Pentester régulièrement : un pentest API GraphQL annuel minimum, complété par des scans automatisés mensuels. Notre équipe recommande un cycle trimestriel pour les API exposant des données sensibles. Pour aller plus loin, consultez notre guide comment faire un pentest de votre application.

Pour les outils de monitoring, Apollo Studio (pour Apollo Server) offre un dashboard natif avec les métriques de performance par opération. Pour les implémentations custom, graphql-armor combine en un seul package le depth limiting, le cost analysis, la protection contre le batching et le logging des requêtes bloquées. Intégrez ces logs dans votre SIEM pour corréler avec les autres sources de sécurité. Pour plus de détails sur la mise en place d’un SIEM, consultez notre guide comment mettre en place un SIEM.

Tableau récapitulatif des 7 étapes

Étape Menace ciblée Outil/Librairie recommandé Effort
1. Introspection Schema leak, reconnaissance Config Apollo/Yoga/Hasura 30 min
2. Depth + Cost DoS par requêtes imbriquées graphql-depth-limit, graphql-query-complexity 2-4 h
3. Auth resolvers IDOR, fuite par traversée graphql-shield, directives @auth 1-3 jours
4. Input validation Injections SQL/NoSQL/XSS graphql-scalars, DOMPurify 1-2 jours
5. Batching control Brute force, exfiltration graphql-armor, config serveur 2-4 h
6. WebSocket auth Fuite via subscriptions graphql-ws, hooks onConnect/onSubscribe 4-8 h
7. Monitoring Détection continue Apollo Studio, SIEM, graphql-armor 1-2 jours

L’effort total pour sécuriser correctement une API GraphQL de complexité moyenne (30 à 80 types, 10 à 40 mutations) se situe entre 5 et 10 jours-développeur. C’est un investissement modéré rapporté au coût d’une fuite de données (en moyenne 4,45 M€ selon IBM, 2025) ou d’une sanction RGPD.

Questions fréquentes

Pourquoi les API GraphQL sont-elles plus vulnérables que les API REST ? +

GraphQL expose un endpoint unique avec un schéma auto-documenté via l’introspection. L’appelant peut construire des requêtes arbitrairement complexes, demander des champs non prévus par le développeur, et combiner des mutations. Sans contrôles explicites, un attaquant peut exfiltrer la totalité du schéma, construire des requêtes profondément imbriquées pour provoquer un déni de service, ou accéder à des champs sensibles par traversée de relations.

Comment désactiver l’introspection GraphQL en production ? +

Avec Apollo Server, ajoutez introspection: false dans la configuration du serveur. Avec graphql-yoga, utilisez le plugin useDisableIntrospection. Avec Hasura, configurez la variable HASURA_GRAPHQL_ENABLE_INTROSPECTION à false. Pour les autres frameworks, vérifiez la documentation de votre implémentation. Conservez l’introspection activée uniquement sur les environnements de développement et staging, jamais en production.

Quel est le coût moyen d’un pentest API GraphQL ? +

Le coût d’un pentest API GraphQL varie selon la taille du schéma et la complexité des resolvers. Pour une API avec 20 à 50 types et 10 à 30 mutations, comptez entre 5 000 et 12 000 euros HT pour un pentest boîte grise de 3 à 5 jours. Pour une API plus complexe avec plus de 100 types, des abonnements WebSocket et des resolvers fédérées, le budget se situe entre 12 000 et 25 000 euros HT. WebGuard Agency propose des forfaits adaptés avec rapport OWASP API Top 10.

Les firewalls applicatifs (WAF) classiques protègent-ils les API GraphQL ? +

Les WAF classiques configurés pour REST offrent une protection très limitée contre les attaques GraphQL. GraphQL utilise un endpoint unique avec des requêtes POST contenant du JSON, ce qui contourne la plupart des règles basées sur les patterns URL. Les WAF modernes comme Cloudflare API Shield, AWS WAF avec règles personnalisées ou des solutions spécialisées comme Inigo ou Stellate proposent des protections GraphQL-aware. La meilleure approche reste la défense en profondeur : WAF en périmètre plus contrôles applicatifs dans les resolvers.

Sécurisez vos API GraphQL avec nos experts

WebGuard Agency propose des pentests API GraphQL complets, des audits de schéma et un accompagnement à la remédiation. Nos pentesters certifiés OSCP, OSWE et GWAPT combinent tests manuels et outils spécialisés pour couvrir l’intégralité de l’OWASP API Security Top 10.

Demander un devis pentest →

🛡️ Audit de sécurité gratuit — réponse en 24h, sans engagement

Obtenir mon audit gratuit →