Injection de sous-graphes fédérés : la fuite de données "aveugle" en GraphQL

En pleine migration vers Federated GraphQL (Supergraphs), une nouvelle vulnérabilité critique a émergé : Injection de sous-graphes fédérés. Alors que les organisations renforcent leurs API Gateways, elles négligent souvent la partie la plus vulnérable — les sous-graphes eux-mêmes. Cette faille exploite la confiance implicite entre le Gateway et ses sous-graphes, permettant aux attaquants de “coudre” ensemble des fragments de données sensibles provenant de microservices privés qui n’étaient pas destinés à être reliés. Cet article explore le fonctionnement de cette attaque, pourquoi elle échappe aux WAF traditionnels, et comment mettre en place une architecture Zero Trust pour votre Data Graph.
La montée en puissance du Supergraph (et la faille de sécurité)
Dans le paysage moderne des API, la fédération GraphQL — popularisée par Apollo Federation, Hasura, et WunderGraph — est devenue la norme pour unifier des dizaines de microservices (sous-graphes) en une seule endpoint interrogeable (le Supergraph ou Gateway).
L’architecture de confiance
Dans une configuration fédérée typique :
- L’utilisateur envoie une requête au Gateway.
- Le Gateway valide le JWT de l’utilisateur, vérifie les permissions et construit un plan de requête.
- Le Gateway divise la requête en fragments et les envoie aux sous-graphes respectifs (par ex., Service Utilisateurs, Service Facturation, Service Inventaire).
- Les sous-graphes exécutent leur partie et renvoient du JSON.
- Le Gateway fusionne (“couds”) les résultats et les renvoie à l’utilisateur.
Le défaut fatal : La plupart des équipes techniques considèrent les sous-graphes comme “internes” et donc “sûrs”. Elles supposent qu’une requête atteignant un sous-graphes a déjà été autorisée par le Gateway. En conséquence, les sous-graphes omettent souvent leurs propres vérifications d’autorisation, créant un problème classique de Confused Deputy.
Qu’est-ce que l’Injection de sous-graphes fédérés ?
L’Injection de sous-graphes fédérés est une vulnérabilité côté serveur où un attaquant manipule la structure de la requête GraphQL pour forcer le Gateway à injecter des fragments malveillants dans un sous-graphes vulnérable.
Parce que le sous-graphes suppose que le Gateway est le seul appelant — et que celui-ci est “intelligent” — il exécute aveuglément des requêtes pour des champs sensibles (comme PII, IDs internes ou drapeaux d’administration) sans vérifier si l’utilisateur initial avait réellement la permission de voir ces champs dans ce contexte précis.
Pourquoi est-ce “aveugle” ?
Le terme “aveugle” fait référence à deux modes d’échec distincts :
Cécité du Gateway : Le Gateway voit une requête syntaxiquement valide. Il vérifie son schéma global et dit, “Ça a l’air correct.” Il ne connaît pas nécessairement les contraintes métier des microservices sous-jacents.
Cécité du sous-graphes : Le sous-graphes reçoit une requête lui disant efficacement, “Donne-moi le SSN pour l’ID utilisateur 123.” Il ne voit pas le token ou le contexte du client original — ou l’ignore — en faisant confiance à la requête simplement parce qu’elle provient de l’IP du Gateway.
Anatomie de l’attaque
Scénario : La fuite d’identité “cousue”
Imaginez un Supergraph avec deux sous-graphes :
- Sous-graphes Utilisateurs : Gère les données de profil public.
- Sous-graphes Facturation : Gère les données privées de carte de crédit et factures.
Le sous-graphes Utilisateurs définit :
type User @key(fields: "id") {
id: ID!
username: String
# Informations publiques uniquement
}
Le sous-graphes Facturation étend le type User :
extend type User @key(fields: "id") {
id: ID! @external
last4Digits: String
invoices: [Invoice]
# SENSIBLE : Seul l’utilisateur lui-même doit voir ceci
internalRiskScore: Int
}
La vulnérabilité
Le Gateway impose que l’utilisateur soit connecté pour interroger invoices. Cependant, le champ internalRiskScore dans le sous-graphes Facturation n’a pas de directive @auth spécifique, car les développeurs pensaient, “C’est un champ interne — le Gateway ne l’exposera pas au schéma public.”
Si la composition du schéma est mal configurée (voir plus bas pour des exemples concrets), ou si l’attaquant utilise Injection de fragments, il peut contourner les vérifications de surface du Gateway.
La requête d’exploitation
query LeakRiskScore {
node(id: "User:123") {
... on User {
username
_onBilling_internalRiskScore: internalRiskScore
}
}
}
Flux d’exécution
- Gateway voit une requête pour un
Node. Il résout l’ID. - Planificateur de requêtes réalise que
internalRiskScorese trouve dans le sous-graphes Facturation. - Injection : Le Gateway génère une requête de récupération vers le sous-graphes Facturation :
query {
_entities(representations: [{ __typename: "User", id: "123" }]) {
... on User {
internalRiskScore
}
}
}
- Sous-graphes Facturation reçoit la requête. Il voit une demande pour
internalRiskScorepour l’ID 123. Il ne vérifie pas si l’utilisateur actuel est bien l’User 123. Il suppose que le Gateway a déjà vérifié cela. - Fuite : Le sous-graphes Facturation retourne le score
99(Risque élevé). - Réponse : L’attaquant reçoit les données privées.
CVEs réels : ce n’est plus théorique
Ce pattern d’attaque est désormais bien documenté. Fin 2025, plusieurs CVEs de haute gravité ont été divulgués contre Apollo Federation — le runtime fédéré GraphQL le plus déployé.
CVE-2025-64530 — Contournement du contrôle d’accès aux interfaces (novembre 2025)
Gravité : Élevée
Une vulnérabilité dans la logique de composition d’Apollo Federation (versions antérieures à 2.9.5, 2.10.4, 2.11.5, et 2.12.1) permettait de contourner les contrôles d’accès sur les types et champs d’interface. Apollo Federation autorisait incorrectement les directives de contrôle d’accès utilisateur (@authenticated, @requiresScopes, @policy) sur les types d’interface. Or, la spécification GraphQL ne définit pas de règles d’héritage pour ces directives entre interfaces et types concrets, donc ces directives n’étaient pas propagées aux implémentations concrètes.
Un attaquant pouvait interroger les données protégées via le type d’objet implémenté en utilisant un fragment inline ou nommé, contournant totalement le contrôle sur l’interface. La mise à jour a rejeté les directives de contrôle d’accès utilisateur sur les types d’interface, et la logique de composition de Federation génère automatiquement les directives correctes sur les implémentations.
Mitigation : Mettre à jour vers Apollo Federation composition 2.9.5+, 2.10.4+, 2.11.5+, ou 2.12.1+. Si vous utilisez une version non corrigée, copiez manuellement les exigences de contrôle d’accès des interfaces vers chaque type d’implémentation.
CVE-2025-64173 — Échec d’autorisation pour types polymorphes
Gravité : Élevée
Dans les versions 1.61.11 et inférieures (et 2.0.0-alpha.0 à 2.8.1-rc.0) d’Apollo Router, la gestion des directives de contrôle d’accès sur les types d’interface et leurs types d’objets implémentés était incorrecte. Lorsque toutes les implémentations partageaient les mêmes exigences, le routeur appliquait les directives sur l’interface tout en ignorant celles sur les types d’objets. Cela permettait à des requêtes non authentifiées d’accéder à des données nécessitant des contrôles supplémentaires.
Mitigation : Mettre à jour Apollo Router vers 1.61.12 ou 2.8.1.
CVE-2025-64347 — Contournement de directive renommée
Gravité : Élevée
Dans les versions 1.61.12-rc.0 et inférieures, les directives de contrôle d’accès renommées via @link n’étaient pas appliquées. Si une équipe avait aliasé @authenticated sous un nom personnalisé dans leur schéma, le routeur ne l’appliquait pas — laissant le champ sans protection. La correction a été intégrée dans les versions 1.61.12 et 2.8.1.
GHSA-m8jr-fxqx-8xx6 — Contournement transitive du contrôle d’accès aux champs
Une vulnérabilité supplémentaire dans la logique de composition d’Apollo Federation ne vérifiait pas que les champs dépendant de données protégées via @requires ou @fromContext héritaient des mêmes exigences de contrôle. Un attaquant pouvait interroger un champ dépendant d’un champ protégé sans déclencher la vérification, car seul le champ dépendant était demandé — pas le champ protégé lui-même. La correction impose que les champs dépendants respectent les mêmes règles que ceux qu’ils référencent.
Variante d’attaque : Exposition directe du sous-graphes (le “Shadow Graph”)
Même lorsque le Gateway est parfaitement sécurisé, de nombreuses organisations exposent accidentellement leurs endpoints de sous-graphes (ex. billing-service.internal:4000/graphql) via :
- Mauvaise configuration des load balancers ou ingress
- Vulnérabilités SSRF dans des services adjacents
- Noms DNS internes prévisibles accessibles depuis une DMZ
Selon la documentation de sécurité d’Apollo, lorsque les sous-graphes sont directement accessibles, cela contourne complètement la sécurité du routeur. La requête _entities — la requête spéciale utilisée par le Gateway pour récupérer des données fédérées — devient un mécanisme d’accès aux données non authentifié pour quiconque peut atteindre l’endpoint.
Si un attaquant peut atteindre directement le sous-graphes, il peut simuler une requête Gateway :
POST http://billing-service.internal:4000/graphql
{
"_entities": [{ "__typename": "User", "id": "456" }],
"query": "query { _entities(representations: [{ __typename: \"User\", id: \"456\" }]) { ... on User { internalRiskScore invoices { amount } } } }"
}
Pas de JWT. Pas d’en-tête d’authentification. Juste un accès interne brut.
Pourquoi les WAF traditionnels ne détectent pas cela
Un Web Application Firewall (WAF) fonctionne principalement sur la signature des requêtes HTTP — motifs d’URL, payloads, chaînes malveillantes connues. L’Injection de sous-graphes fédérés produit :
- Des requêtes GraphQL syntaxiquement valides
- Des requêtes
POST /graphqlstandards avecContent-Type: application/json - Aucun SQL, commandes shell, ou payloads classiques d’injection
- Des requêtes qui “réussissent” du point de vue des logs du Gateway, affichant une réponse
200 OK
Cela signifie que les logs du Gateway ne montrent aucune anomalie. La faille est invisible à chaque niveau sauf au sous-graphes lui-même — qui, par conception, n’a aucune conscience que la requête était non autorisée. C’est le cœur du problème de conformité : un audit GDPR ou CCPA montrera une requête utilisateur légitime retournant un 200, sans trace des champs spécifiques du sous-graphes réellement renvoyés.
Défense en profondeur : sécuriser le Supergraph
Passer d’une “confiance au Gateway” à une “Zero Trust” nécessite des contrôles à chaque couche.
1. Autorisation au niveau du sous-graphes (La règle d’or)
Ne jamais se fier uniquement au Gateway pour l’autorisation. Chaque sous-graphes doit valider indépendamment l’identité de l’utilisateur et ses droits d’accès à un objet précis.
// Dans le résolveur du sous-graphes Facturation
const resolvers = {
User: {
internalRiskScore: async (userEntity, args, context) => {
// CRUCIAL : Vérifier l’autorisation au niveau du résolveur
if (context.userId !== userEntity.id && !context.isAdmin) {
throw new AuthenticationError("Non autorisé à voir le score de risque");
}
return fetchRiskScore(userEntity.id);
}
}
};
L’objet context doit être alimenté en propageant le JWT original de l’utilisateur du Gateway vers le sous-graphes à chaque requête. C’est non négociable.
2. Sécuriser la communication Gateway-vers-sous-graphes
Les sous-graphes ne doivent accepter que les requêtes du Gateway légitime. Deux options fortes :
Mutual TLS (mTLS) : La meilleure défense. Les sous-graphes acceptent uniquement les connexions avec un certificat client délivré au Gateway. Toute requête directe — d’un attaquant ou d’un SSRF mal configuré — est rejetée lors de la poignée de main TLS.
Requêtes signées HMAC : Le Gateway signe chaque requête avec un secret partagé. Le sous-graphes vérifie la signature avant traitement. Note : une simple en-tête statique comme x-gateway-secret: "valeur-codée" n’est pas suffisante — les secrets peuvent fuiter dans le code source, les logs, ou les dumps d’environnement.
3. Isolation réseau — garder les sous-graphes internes
Comme le souligne la documentation de sécurité officielle d’Apollo, les sous-graphes doivent être accessibles uniquement depuis le Router. Ils ne doivent jamais être accessibles depuis Internet public, ni depuis des segments DMZ, ni depuis des services autres que le Gateway. Appliquer cela au niveau réseau (groupes de sécurité, politiques VPC, objets Kubernetes NetworkPolicy) — pas seulement par convention.
4. Désactiver _service et _entities sur les endpoints publics
Le champ _service expose le SDL brut (Schema Definition Language) du sous-graphes. Le champ _entities est le point d’entrée pour les requêtes fédérées. Si votre architecture nécessite absolument que le sous-graphes soit accessible depuis l’extérieur du Gateway (rare et non recommandé), utilisez un middleware pour bloquer ces champs sauf si la requête provient d’une IP Gateway de confiance.
5. Patch et mise à jour
Les CVEs ci-dessus montrent que même les runtimes fédérés bien conçus peuvent avoir des failles de logique de composition. Vous devriez :
- Suivre les avis de sécurité pour Apollo Router, la composition d’Apollo Federation, et toute autre librairie fédérée utilisée.
- Exécuter
apollo checketrover subgraph checkdans votre pipeline CI/CD pour détecter les régressions de contrôle d’accès avant déploiement. - Utiliser des outils comme GraphQL Inspector ou Apollo GraphOS schema checks pour repérer les champs publics qui retournent des types sensibles.
6. Appliquer le contrôle d’accès sur @requires et @fromContext
Suite à GHSA-m8jr-fxqx-8xx6, auditez votre schéma pour tout champ utilisant @requires ou @fromContext dépendant de données protégées. Assurez-vous que ces champs dépendants portent des directives de contrôle d’accès correspondantes. La librairie de composition corrigée appliquera cela automatiquement, mais les versions plus anciennes doivent être auditées manuellement.
7. Analyse du coût des requêtes au niveau du sous-graphes
Les attaquants peuvent aussi utiliser l’injection fédérée pour lancer des attaques de type Denial of Service en demandant des relations fédérées profondément imbriquées. Implémentez une limitation de profondeur de requête et une analyse de coût au niveau du sous-graphes — pas seulement au Gateway — pour éviter l’épuisement des ressources.
Impact commercial et conformité
Les conséquences d’une injection de sous-graphes fédérés réussie dépassent largement la fuite de données immédiate.
Fuite de données : Les données personnelles (emails, IDs, données financières) peuvent être exfiltrées entre microservices, même ceux supposés protégés.
Violations de conformité : Comme les logs du Gateway montrent un 200 OK pour une requête légitime, il n’y a pas de trace d’accès non autorisé. Les audits GDPR ou CCPA peuvent constater que l’accès à certains champs n’était pas contrôlé, même si une couche d’authentification était en place.
Atteinte à la réputation : Les architectures fédérées sont adoptées pour leur scalabilité. Une faille dans cette couche peut impacter toutes les données dans le Supergraph.
Résumé
GraphQL fédéré offre une grande vélocité aux développeurs, mais fracture la sécurité périmétrique traditionnelle. L’Injection de sous-graphes fédérés prospère dans l’écart entre le “Smart Gateway” et le “Dumb Sub-graph” — et comme le montrent les CVEs de fin 2025, même la logique de composition des principaux runtimes fédérés peut introduire des contournements de contrôle d’accès invisibles aux outils de sécurité classiques.
Le principe à retenir est simple : la logique d’autorisation doit voyager avec les données, pas seulement avec la requête. Les équipes de sécurité doivent auditer leurs Supergraphs aujourd’hui, traiter chaque sous-graphes comme un service en façade publique du point de vue de l’autorisation, et s’assurer que le contrôle d’accès est appliqué de manière indépendante à chaque couche du graphe.
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.