Security
9 min read
1107 views

Context Mixing : Exploitation de @ExecutionContext dans les modules GraphQL

IT
InstaTunnel Team
Published by our engineering team
Context Mixing : Exploitation de @ExecutionContext dans les modules GraphQL

GraphQL a évolué pour devenir la colonne vertébrale des couches de données fédérées et modulaires dans l’architecture web moderne. Mais à mesure que les couches d’abstraction deviennent plus sophistiquées, les surfaces d’attaque potentielles augmentent également. Un de ces risques se situe à l’intersection de l’injection de dépendances, du JavaScript asynchrone et de l’état singleton partagé — et il est plus dangereux que ce que la plupart des équipes réalisent.

Ce post décompose une classe critique de vulnérabilités dans le framework GraphQL Modules, explique la mécanique réelle derrière, la relie à un CVE récemment corrigé de Node.js qui prouve que la menace n’est pas théorique, et vous donne des étapes concrètes pour protéger votre API.


Qu’est-ce que GraphQL Modules ?

GraphQL Modules est une boîte à outils maintenue par The Guild qui permet aux équipes de construire des modules réutilisables, maintenables et testables pour les serveurs GraphQL. Au cœur se trouve un système puissant d’injection de dépendances qui permet aux développeurs de définir des “Providers” — des services responsables de la logique métier, de la récupération de données et de l’autorisation.

Une de ses fonctionnalités les plus utiles est le décorateur @ExecutionContext.


Le rôle de @ExecutionContext

Dans une exécution GraphQL standard, un objet context est transmis à chaque résolveur. Il transporte généralement la session de l’utilisateur actuel, les jetons d’authentification et les connexions à la base de données.

Le point sensible : lorsque vous travaillez avec des Providers Singleton — des services instanciés une seule fois pour toute la durée de vie de l’application — vous ne pouvez pas accéder directement au contexte par requête. Le décorateur @ExecutionContext a été conçu pour combler cette lacune. Il permet à un service Singleton d’accéder dynamiquement au contexte de l’exécution en cours, généralement en exploitant le suivi asynchrone des ressources de Node.js.

Comme le décrivent les docs officielles de GraphQL Modules :

e “Grâce au décorateur @ExecutionContext, chaque provider Singleton a accès au contexte GraphQL et à l’injecteur à portée d’opération.”

Cela semble propre. En pratique, sous charge concurrente, c’est une arme chargée.


La vulnérabilité : Mélange de contexte sous charge concurrente

La vulnérabilité est une condition de course (CWE-362) qui apparaît lorsque plusieurs requêtes parallèles touchent un service Singleton utilisant @ExecutionContext.

Cause racine : Pollution d’état dans le conteneur DI

Le problème provient d’une isolation inadéquate du contexte d’exécution à l’intérieur du conteneur DI. Lorsqu’au moins deux requêtes sont traitées simultanément, le framework peut échouer à synchroniser correctement l’injection du contexte dans l’instance de service partagé.

Sous forte charge, considérez ce scénario :

  • Utilisateur A (administrateur) et Utilisateur B (utilisateur régulier) envoient des requêtes presque simultanément.
  • Les deux requêtes invoquent un service Singleton utilisant @ExecutionContext.
  • Le framework assigne le contexte de l’utilisateur A au Singleton.
  • La résolution de l’utilisateur A atteint un await — appel à la base de données, requête API, tout appel asynchrone.
  • Pendant que l’utilisateur A est suspendu au await, la boucle d’événements de Node.js récupère la requête de l’utilisateur B.
  • Étant donné que l’instance Singleton est partagée, sa référence interne context est maintenant écrasée — ou pire, le contexte élevé de l’utilisateur A se propage dans le chemin d’exécution de l’utilisateur B.

Il s’agit d’une erreur classique de type TOCTOU (Time-of-Check Time-of-Use). Le service lit le contexte à un instant donné, mais au moment d’agir sur ce contexte pour une opération sensible, la valeur a été échangée par une requête concurrente.

Pourquoi Node.js aggrave cela

Node.js est mono-thread mais gère la concurrence via la boucle d’événements et les callbacks asynchrones. Le module async_hooks (et son successeur moderne AsyncLocalStorage) est conçu pour suivre le contexte d’exécution à travers les frontières asynchrones. Cependant, chaque await crée un “point de suspension” — une fenêtre où la boucle d’événements peut traiter une autre requête et écraser les références partagées dans un Singleton.

Les issues GitHub de GraphQL Modules ont documenté des cas réels en production où l’injection accidentelle d’un service à contexte de session dans un provider à portée d’application (Singleton) causait des fuites de mémoire et une contamination d’état. Le mélange de contexte est la forme d’impact de sécurité de la même erreur architecturale.


Cas réel : CVE-2025-59466

Ce n’est pas une classe de bug purement théorique. Le 13 janvier 2026, l’équipe de sécurité de Node.js a corrigé CVE-2025-59466 — une faille critique dans async_hooks qui rôdait dans le code depuis neuf ans.

La vulnérabilité montrait que lorsqu’un débordement de pile se produit dans un callback async_hooks (provoqué par une entrée récursive profondément imbriquée), l’environnement d’exécution invoquait un gestionnaire d’erreur fatale et terminait tout le processus serveur — sans possibilité pour le code utilisateur de capturer l’erreur. Toutes les requêtes en cours étaient abandonnées.

Plus pertinent pour le mélange de contexte : cette CVE révélait que le suivi de contexte basé sur async_hooks est fragile dans des conditions adverses. Tout ce qui perturbe la chaîne d’exécution asynchrone normale — nesting extrême, résolution récursive ou surcharge à haute concurrence — peut corrompre la propagation du contexte sur laquelle les frameworks comme GraphQL Modules comptent.

Versions affectées de Node.js : 8.x à 18.x (toutes en fin de vie et non corrigées). Versions corrigées : 20.20.0, 22.22.0, 24.13.0, et 25.3.0.

De plus, l’équipe Node.js a officiellement marqué l’API async_hooks comme Stabilité 1 - Expérimental, recommandant fortement la migration vers AsyncLocalStorage. Dans Node.js 24+, AsyncLocalStorage a été réimplémenté sur V8 avec AsyncContextFrame, supprimant sa dépendance à async_hooks.createHook() et éliminant toute cette classe de fragilité.

La vulnérabilité de mélange de contexte dans GraphQL Modules se trouve dans ce même voisinage : une abstraction au niveau du framework qui fait confiance à l’isolation du contexte basée sur async_hooks pour tenir sous charge concurrente.


Scénario d’exploitation : Escalade de privilèges dans une application financière

Imaginez une application financière où un administrateur traite en lot la paie pendant qu’un utilisateur malveillant rafraîchit simplement sa page de profil.

La configuration

L’application utilise un FinanceService pour autoriser les transactions. Pour la performance, il est déclaré comme Singleton et utilise @ExecutionContext pour récupérer le jeton de l’appelant :

import { Injectable, ExecutionContext, Scope } from 'graphql-modules';

@Injectable({ scope: Scope.Singleton })
class FinanceService {
  @ExecutionContext()
  private context: ExecutionContext;

  async transferFunds(amount: number) {
    // Point de défaillance sous charge concurrente
    const token = this.context.injector.get(AUTH_TOKEN);

    return await bankApi.post('/transfer', { amount }, {
      headers: { Authorization: `Bearer ${token}` }
    });
  }
}

La vecteur d’attaque

L’attaquant n’a pas besoin de compétences avancées. Un simple script — ou même Burp Suite’s Intruder — envoyant une série de requêtes lors d’une session administrateur connue suffit. L’objectif est purement temporel.

La collision

  1. L’utilisateur A (Admin) déclenche une mutation sensible transferFunds.
  2. Le framework définit @ExecutionContext sur le contexte de l’Admin dans le Singleton.
  3. La résolution de l’utilisateur A atteint l’appel await bankApi.post(...) et se suspend.
  4. La boucle d’événements de Node.js récupère la requête de l’utilisateur B.
  5. En raison de la condition de course dans la référence de contexte du Singleton, l’exécution de l’utilisateur B écrase ou hérite du jeton d’admin de l’utilisateur A.
  6. La requête de l’utilisateur B exécute maintenant transferFunds avec l’en-tête Authorization de l’Admin.
  7. La gestion des rôles et permissions est totalement contournée. Les logs d’audit montrent la transaction comme si elle venait de l’Admin.

Modèle de preuve de concept

Les chercheurs en sécurité valident cette classe de bug en injectant une promesse différée — en suspendant intentionnellement l’exécution d’une requête à un point await, puis en lançant une course avec une seconde requête pour démontrer que la référence de contexte du Singleton change. Avec un test basé sur le timing, le changement de contexte est reproductible de manière fiable à haute concurrence.


Évaluation de l’impact

La gravité de cette classe de vulnérabilité est ÉLEVÉE. L’impact concerne à la fois la Confidentialité et l’Intégrité.

Zone de risque Impact potentiel
Accès non autorisé aux données Les utilisateurs peuvent voir des PII, des dossiers financiers ou des messages privés appartenant à d’autres
Escalade de privilèges Un utilisateur à faible privilège peut effectuer des actions administratives sous l’identité d’un autre
Prise de contrôle de compte Les jetons de session divulgués peuvent permettre un accès prolongé après la fin de la fenêtre de course
Corruption de la piste d’audit Des mutations malveillantes apparaissent dans les logs comme si elles avaient été effectuées par la victime

Remédiation et bonnes pratiques

1. Mettre à jour GraphQL Modules

Si vous utilisez une version vulnérable, appliquez la correction immédiatement.

npm install graphql-modules@latest
# ou
yarn add graphql-modules@latest

La correction consiste à réarchitecturer la façon dont @ExecutionContext suit l’injecteur interne, en garantissant que les références de contexte ne soient jamais partagées entre les frontières asynchrones.

Versions corrigées : - graphql-modules ≥ 2.4.1 - graphql-modules ≥ 3.1.1 - @envelop/graphql-modules ≥ 9.1.0

2. Mettre à niveau Node.js

Passez à une des versions corrigées de Node.js : 20.20.0, 22.22.0, 24.13.0, ou 25.3.0. Ces versions corrigent la fragilité sous-jacente de async_hooks. Si vous pouvez passer à Node.js 24+, AsyncLocalStorage ne dépend plus de async_hooks.createHook().

3. Favoriser le scope d’opération plutôt que Singleton pour les services sensibles à l’authentification

C’est la mitigation la plus structurante. La documentation de GraphQL Modules précise que le scope d’opération ne se chevauche pas entre les requêtes : pour trois requêtes simultanées, trois instances de service isolées sont créées — une par requête.

import { Injectable, Scope, createModule } from 'graphql-modules';

@Injectable({ scope: Scope.Operation }) // Par requête, totalement isolé
class FinanceService {
  constructor(private authContext: AuthContext) {}

  async transferFunds(amount: number) {
    const token = this.authContext.token; // Sûr : lié à cette requête uniquement
    return await bankApi.post('/transfer', { amount }, {
      headers: { Authorization: `Bearer ${token}` }
    });
  }
}

Oui, cela a un léger coût de performance — les services sont instanciés par opération plutôt que réutilisés. Pour les services gérant l’authentification ou la décision d’autorisation, c’est le compromis correct. Les singletons doivent être réservés aux services véritablement sans état, indépendants de la requête : lecteurs de configuration, caches avec leur propre isolation, gestionnaires de pools de connexions, etc.

4. Pousser l’authentification dans un middleware global, pas dans les internals du service

Au lieu que les services “tirent” le contexte utilisateur via un décorateur (qui repose sur le bon câblage du contexte par le runtime), validez et attachez les données d’autorisation avant que l’exécution n’atteigne le conteneur DI. Un plugin Envelop ou un middleware Express est l’endroit approprié pour cela :

const useAuthentication = () => ({
  onExecute({ args }) {
    const token = args.contextValue.token;
    if (!token || !isValid(token)) {
      throw new GraphQLError('Non autorisé', {
        extensions: { code: 'UNAUTHENTICATED' }
      });
    }
  }
});

Ce modèle “push” élimine complètement la possibilité de mélange de contexte au niveau du décorateur.

5. Implémenter des IDs de corrélation de requête pour la détection

Même si vous ne pouvez pas corriger immédiatement, vous pouvez détecter le mélange de contexte dans vos logs. Attribuez un Request-ID unique à chaque requête entrante et incluez-le dans toutes les lignes de logs tout au long du cycle de vie de cette requête. Si vos logs montrent un seul Request-ID associé à plus d’un User-ID, vous avez une preuve de contamination croisée.

app.use((req, res, next) => {
  req.requestId = crypto.randomUUID();
  res.setHeader('X-Request-ID', req.requestId);
  next();
});

La leçon plus large : Le partage d’état dans les environnements asynchrones est dangereux

CVE-2025-59466 et le risque de mélange de contexte dans GraphQL Modules pointent tous deux vers une même vérité fondamentale : le suivi du contexte asynchrone est difficile à faire correctement, et les abstractions “magiques” qui s’y appuient héritent de cette fragilité.

La documentation de Node.js marque désormais l’API async_hooks comme expérimentale et recommande de ne pas l’utiliser. L’équipe Node.js déconseille fortement l’usage de async_hooks, soulignant que les cas d’usage du suivi de contexte asynchrone sont mieux servis par l’API stable AsyncLocalStorage. Les frameworks construits sur async_hooks avant la maturation de AsyncLocalStorage portent une dette technique pouvant se manifester sous forme de vulnérabilités de sécurité en cas de concurrence adversariale.

La règle pour les développeurs est simple et absolue : l’état appartenant à un utilisateur ne doit jamais vivre dans un Singleton.

Si vous utilisez @ExecutionContext aujourd’hui, auditez chaque provider Singleton qui l’utilise. Demandez-vous : que se passe-t-il si cette référence de contexte est incorrecte ? Si la réponse concerne des jetons d’authentification, des vérifications de permissions ou des données sensibles — ce service ne doit pas être un Singleton.


Aller plus loin

La faille de mélange de contexte est l’une des menaces émergentes dans l’écosystème GraphQL. D’autres domaines à auditer dans votre stack incluent les attaques par complexité de requête (profondeur de résolveur non limitée menant à un DoS), les exploits de batching via l’aliasing natif de GraphQL, l’exposition à l’introspection en production, et la classe plus large de bugs TOCTOU dans tout framework DI qui relie l’état singleton et l’état à portée de requête à travers une frontière asynchrone.

Vos résolveurs ne sont aussi sécurisés que le contexte en lequel ils font confiance.

Continue from this article into the most relevant product guides and workflows.

Related Topics

#Context mixing, GraphQL context mixing, CVE-2026-23735, GraphQL Modules vulnerability, @ExecutionContext, GraphQL race condition, GraphQL concurrency bug, authentication context confusion, privilege escalation GraphQL, parallel request vulnerability, request context leak, security context mixup, cross-request contamination, GraphQL authorization bypass, GraphQL auth bug, GraphQL multi-tenant vulnerability, GraphQL isolation failure, async context bug, thread safety bug GraphQL, Node.js async context leak, request scope vulnerability, dependency injection context bug, GraphQL framework vulnerability, GraphQL gateway security, API context poisoning, API race condition exploit, accidental privilege escalation, low to admin escalation, context propagation bug, request lifecycle bug, GraphQL server misconfiguration, GraphQL execution pipeline bug, parallel resolver execution risk, GraphQL security 2026, GraphQL CVE analysis, GraphQL auth middleware flaw, security decorator bug, ExecutionContext exploit, cross-user data leak GraphQL, GraphQL session confusion, token mixup vulnerability, JWT context bug, access control race condition, TOCTOU GraphQL, concurrent request attack, API privilege escalation, GraphQL threat model, GraphQL pentesting, GraphQL red team, GraphQL blue team defense, fix context isolation, per-request context isolation, secure GraphQL architecture, framework-level auth bug, microservices context leak, multi-user concurrency bug, API trust boundary failure, GraphQL incident response, GraphQL patching guidance

Keep building with InstaTunnel

Read the docs for implementation details or compare plans before you ship.

Share this article

More InstaTunnel Insights

Discover more tutorials, tips, and updates to help you build better with localhost tunneling.

Browse All Articles