Security
9 min read
1108 views

Context Mixing: @ExecutionContextを悪用したGraphQLモジュールの脆弱性

IT
InstaTunnel Team
Published by our engineering team
Context Mixing: @ExecutionContextを悪用したGraphQLモジュールの脆弱性

GraphQLは現代のWebアーキテクチャにおいて、フェデレーションやモジュール化されたデータ層の基盤として成熟しています。しかし、抽象化レイヤーが高度になるにつれて、その下に隠れた攻撃面も複雑化しています。その一つが、Dependency Injection、非同期JavaScript、共有シングルトン状態の交差点に存在し、多くのチームが気付いていない危険性です。

この記事では、GraphQL Modulesフレームワークにおける重要な脆弱性のクラスを解説し、その仕組みと現実の事例、最近修正されたNode.js CVEとの関連性を示し、APIを守る具体的な対策を紹介します。


GraphQL Modulesとは?

GraphQL Modulesは、The Guildが開発したツールキットで、チームが再利用可能でメンテナンスしやすくテスト可能なGraphQLサーバーモジュールを構築できるようにします。コアには強力なDependency Injectionシステムがあり、「Providers」と呼ばれるサービスを定義し、ビジネスロジック、データ取得、認証を担当させます。

その中でも特に便利なのが@ExecutionContextデコレーターです。


@ExecutionContextの役割

標準的なGraphQLの実行では、contextオブジェクトが各リゾルバーを通じて渡されます。これには通常、現在のユーザーのセッション、認証トークン、データベース接続などが含まれます。

問題は、シングルトンプロバイダー — アプリケーションのライフタイム中に一度だけインスタンス化されるサービス — で作業している場合です。これらのサービスはリクエストごとのコンテキストに直接アクセスできません。@ExecutionContextは、そのギャップを埋めるために設計されており、シングルトンサービスが現在の実行のコンテキストに動的にアクセスできるようにします。これは、Node.jsの非同期リソース追跡を利用しています。

GraphQL Modulesの公式ドキュメントでは次のように説明されています:

”@ExecutionContextデコレーターのおかげで、すべてのシングルトンプロバイダーはGraphQLコンテキストと操作スコープのインジェクターにアクセスできます。”

これはクリーンに見えますが、実際には並行負荷下では危険な状態になることがあります。


脆弱性の概要:並行負荷時のコンテキストの混合

この脆弱性は、レースコンディション(CWE-362)として知られ、複数の並列リクエストが@ExecutionContextを使用するシングルトンサービスに同時にアクセスしたときに発生します。

根本原因:DIコンテナ内の状態汚染

問題は、DIコンテナ内の実行コンテキストの隔離が不十分なことにあります。複数のリクエストが同時に処理されると、フレームワークはコンテキストのインジェクションを適切に同期できず、共有されたサービスインスタンスにコンテキストが誤って注入される可能性があります。

高負荷時のシナリオ例:

  • ユーザーA(管理者)とユーザーB(一般ユーザー)がほぼ同時にリクエストを送信
  • 両者とも@ExecutionContextを使用するシングルトンサービスを呼び出し
  • フレームワークはユーザーAのコンテキストをシングルトンに割り当て
  • ユーザーAのリゾルバーがawait(データベース呼び出しやAPIリクエストなどの非同期処理)に到達
  • ユーザーAがawaitで一時停止している間に、Node.jsのイベントループがユーザーBのリクエストを処理
  • シングルトンインスタンスは共有されているため、その内部のcontext参照が上書きされるか、あるいはユーザーAの権限の高いコンテキストがユーザーBの実行に混入

これは典型的なTOCTOU(Time-of-Check to Time-of-Use)バグです。サービスは一時点でコンテキストを読み取りますが、その後の操作時には値が別のリクエストによって書き換えられている可能性があります。

Node.jsがこの問題を悪化させる理由

Node.jsはシングルスレッドですが、イベントループと非同期コールバックを通じて並行処理を行います。async_hooksモジュール(およびその後継のAsyncLocalStorage)は、非同期境界を越えた実行コンテキストの追跡を目的としていますが、awaitは”一時停止点”を作り出し、その間に他のリクエストが処理され、共有参照が上書きされるリスクを孕んでいます。

GitHubのGraphQL Modulesのissueでは、セッションスコープのサービスをアプリケーションスコープのシングルトンに誤って注入した結果、メモリリークや状態のクロスコンタミネーションが発生した事例が報告されています。コンテキストの混合は、まさに同じアーキテクチャの誤りによるセキュリティリスクです。


実例:CVE-2025-59466

これは純粋な理論的バグではありません。2026年1月13日にNode.jsのセキュリティチームがCVS-2025-59466を修正しました。これはasync_hooksの深刻な脆弱性で、9年間コードベースに潜んでいました。

この脆弱性は、async_hooksのコールバック内でスタックオーバーフローが発生した場合(深くネストされた再帰入力によるもの)、ランタイムが致命的なエラーハンドラーを呼び出し、サーバー全体を終了させるものでした。これにより、リクエストはすべて失われました。

特にコンテキストの混合に関して重要なのは、このCVEがasync_hooksを用いたコンテキスト追跡が脆弱であることを明らかにした点です。極端なネストや再帰解決、高負荷時のハンマリングなど、非同期実行チェーンを妨害する要素は、GraphQL Modulesのようなフレームワークの信頼性を損ないます。

影響を受けるNode.jsバージョン: 8.x〜18.x(すべてサポート終了・未修正)。修正版:20.20.022.22.024.13.025.3.0

さらに、Node.jsチームはasync_hooksAPIをStability 1 - Experimentalと公式にマークし、AsyncLocalStorageへの移行を強く推奨しています。Node.js 24以降では、AsyncLocalStorageはV8のAsyncContextFrame上に再実装され、async_hooks.createHook()への依存を排除し、この脆弱性の根本的な原因を解消しています。

GraphQL Modulesのコンテキスト混合脆弱性は、この同じ領域に存在します。フレームワークレベルの抽象化がasync_hooksに依存し、並行負荷下でも安全に動作すると信じている点です。


攻撃シナリオ:金融アプリケーションにおける権限昇格

管理者が給与処理をバッチで行っている間に、悪意のあるユーザーがプロフィールページをリフレッシュしている状況を想像してください。

セットアップ

アプリはFinanceServiceを使用して取引を認証します。パフォーマンス向上のため、これをシングルトンとして宣言し、@ExecutionContextを使って呼び出し元のトークンを取得します:

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

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

  async transferFunds(amount: number) {
    // ここが並行負荷下での脆弱ポイント
    const token = this.context.injector.get(AUTH_TOKEN);

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

攻撃手法

高度なスキルは不要です。簡単なスクリプトやBurp SuiteのIntruderを使って、管理者セッション中に大量のリクエストを送るだけで十分です。目的はタイミングだけです。

レースコンディション

  1. ユーザーA(管理者)が敏感なtransferFundsを呼び出す
  2. フレームワークが@ExecutionContextを管理者のコンテキストに設定
  3. ユーザーAのリゾルバーがawait bankApi.post(...)に到達し、一時停止
  4. イベントループがユーザーBのリクエストを処理
  5. シングルトンのコンテキスト参照のレースにより、ユーザーBの実行が管理者のトークンを上書きまたは継承
  6. ユーザーBのリクエストがtransferFundsを実行し、管理者のAuthorizationヘッダーを持つ
  7. RBAC(役割ベースアクセス制御)が完全に回避され、ログには管理者からの取引として記録される

検証パターン

セキュリティ研究者は、遅延Promiseを注入してこのバグを検証します。await境界でリクエストを一時停止し、二つ目のリクエストをレースさせることで、シングルトンのコンテキスト参照が変わることを示します。タイミングに依存した高並行性のテスト環境下では、コンテキストの入れ替えは確実に再現可能です。


影響評価

この種の脆弱性の深刻度はです。機密性と完全性の両面に影響します。

リスク領域 潜在的影響
不正アクセス ユーザのPIIや財務記録、プライベートメッセージが閲覧可能
権限昇格 低権限のユーザが管理者権限の操作を実行
アカウント乗っ取り 流出したセッショントークンが長期間アクセスを維持
監査証跡の改ざん 改ざんされた操作が被害者のものとして記録される

対策とベストプラクティス

1. GraphQL Modulesのアップデート

脆弱なバージョンを使用している場合は、即座にパッチを適用してください。

npm install graphql-modules@latest
# または
yarn add graphql-modules@latest

修正は、@ExecutionContextが内部のインジェクターを追跡する方法を再設計し、コンテキストの参照が非同期境界を越えて共有されないようにすることです。

修正版バージョン: - graphql-modules ≥ 2.4.1 - graphql-modules ≥ 3.1.1 - @envelop/graphql-modules ≥ 9.1.0

2. Node.jsのアップグレード

修正版のNode.jsバージョン(20.20.022.22.024.13.025.3.0)にアップグレードしてください。これらはasync_hooksの脆弱性に対処しています。Node.js 24以降では、AsyncLocalStorageasync_hooks.createHook()に依存せず、より堅牢になっています。

3. 認証に関わるサービスはOperation Scopeを優先

これは最も堅牢な対策です。GraphQL Modulesのドキュメントでは、スコープの設定について明確に述べられています。Operation scopeはリクエスト間で重複しません。並行リクエストごとに、完全に隔離されたサービスインスタンスが作成されます。

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

@Injectable({ scope: Scope.Operation }) // リクエストごとに完全に隔離
class FinanceService {
  constructor(private authContext: AuthContext) {}

  async transferFunds(amount: number) {
    const token = this.authContext.token; // このリクエストにバインドされて安全
    return await bankApi.post('/transfer', { amount }, {
      headers: { Authorization: `Bearer ${token}` }
    });
  }
}

少しパフォーマンスコストは増えますが、認証や権限判断を行うサービスには適切な選択です。シングルトンは、設定リーダーやキャッシュ(内部隔離あり)、コネクションプール管理など、ステートレスでリクエストに依存しないサービスに限定すべきです。

4. 認証情報をグローバルミドルウェアにプッシュ

デコレーターを使ってユーザーコンテキストを”引き出す”のではなく、認証情報を事前に検証・付与し、DIコンテナに渡る前に設定します。これにはEnvelopのプラグインやExpressミドルウェアが適しています。

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

この”プッシュ”モデルにより、デコレーターのレベルでのコンテキスト混合のリスクを排除できます。

5. リクエストIDを付与して検知

すぐにパッチできなくても、ログでコンテキストの混合を検知できます。各リクエストに一意のRequest-IDを割り当て、そのIDをすべてのログに記録します。複数のリクエストで同じRequest-IDと異なるUser-IDが記録されていれば、コンテキストのクロスコンタミネーションの証拠です。

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

より広い教訓:非同期環境での共有状態は危険

CVE-2025-59466とGraphQL Modulesのコンテキスト混合リスクは、根底にある共通の真実を示しています:非同期コンテキストの追跡は難しく、「魔法」の抽象化はその脆弱性を継承するということです。

Node.jsの公式ドキュメントも、async_hooksを実験的APIとし、使用を推奨しません。AsyncLocalStorageの方が安定しており、より安全です。async_hooksを使ったフレームワークは、技術的負債を抱え、悪意ある並行処理の下でセキュリティ脆弱性となる可能性があります。

開発者の絶対的なルールは、ユーザに属する状態は絶対にシングルトンに置いてはいけないということです。

もし@ExecutionContextを使っているなら、すべてのシングルトンプロバイダーを監査し、何か問題があれば見直してください。特に、認証トークンや権限、機密データに関わる場合、そのサービスはシングルトンにすべきではありません。


さらに深く

このコンテキスト混合の脆弱性は、GraphQLエコシステムにおける新たな脅威の一つです。監査すべき他の領域には、クエリの複雑性攻撃(リゾルバの深さ制限なしによるDoS)、GraphQLのネイティブエイリアスを利用したバッチ攻撃、本番環境でのインスペクション露出、そして非同期境界を越えたシングルトンとリクエストスコープの状態を橋渡しするDIフレームワークにおけるTOCTOUバグなどがあります。

あなたのリゾルバーは、信頼するコンテキストの安全性に依存しています。

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