文:Rachid.A
翻訳:Yewlne
01 翻訳テキスト
最近、私は仮名 inzo_ の Yasser Allam と共同で研究を行うことを決定しました。いくつかの潜在的な目標について議論した結果、私たちは Next.js に焦点を当てることに決めました(GitHub で 13 万のスターを持ち、現在毎週 940 万回以上ダウンロードされています)。これは私が非常に馴染みのあるフレームワークで、素晴らしい創作経験があります。これは私の以前の研究成果が証明しています。したがって、この記事の「私たち」は当然、私たち二人を指します。
Next.jsは、Reactに基づいたフル機能のJavaScriptフレームワークで、豊富な機能を備えており、詳細を深く研究するのに理想的な場所です。信念、好奇心、そして粘り強さを持って、私たちは旅に出かけ、あまり知られていない隅々を探索し、その中に隠された宝物を探し求めます。
しばらくして、私たちはミドルウェアの中に重大な問題を発見しました。その影響範囲は広範で、すべてのバージョンが影響を受けており、この脆弱性を利用するのに前提条件は必要ありません——私たちはすぐに詳細を示します。
ディレクトリ
Next.jsのミドルウェア
オーソリティツール:宝物級の古いコード
実行順序は middlewareInfo.name と同じです
授権の神器:昨日は詩となり、今朝は更に価値がある
/src ディレクトリ
最大再帰深度
悪用
承認/オーバーライドのバイパス
CSPを回避する
キャッシュポイズニングによるDoSの実現(何?)
闡
セキュリティに関するお知らせ - CVE-2025-29927
免責事項
エピローグ
Next.js ミドルウェア
ミドルウェアは、リクエストが完了する前にコードを実行することを許可します。その後、受信したリクエストに基づいて、リスポンスの内容を修正することができます(Next.js ドキュメントから抜粋)。
完全なフレームワークとして、Next.js は独自のミドルウェアを備えています。これは重要で広く使用されている機能です。その適用シーンは多岐にわたり、最も重要なものには以下が含まれます:
パスの書き換え
サーバー側のリダイレクト
レスポンスにヘッダー情報(CSPなど)要素を追加する
最も重要なのは、認証と承認です
ミドルウェアの一般的な用途の一つは認証を行うことであり、これは特定の条件に基づいて特定のパスを保護することに関係しています。
認証と承認:特定のページまたはAPIルートへのアクセス権を付与する前に、ユーザーの身元を確認し、セッションクッキーをチェックします(Next.jsドキュメント)。
例:ユーザーが /dashboard/admin にアクセスしようとすると、リクエストはまずミドルウェアを通過します。ミドルウェアは、ユーザーのセッションクッキーが有効であり、必要な権限を持っているかどうかを確認します。検証が成功した場合、ミドルウェアはリクエストを転送します。そうでない場合、ミドルウェアはユーザーをログインページにリダイレクトします:
権限ツール:宝物級の古いコード
偉大な人物がかつて言ったように、「話は安い、バグを見せてくれ」、私たちは過度の説明を避け、直接本題に入ります;フレームワークの旧バージョン(v12.0.7)をブラウズしていると、以下のコードを発見しました:
Next.jsアプリケーションがミドルウェアを使用すると、runMiddleware 関数が呼び出されます。 main 関数に加えて、この関数は x-middleware-subrequest ヘッダーの値を受け取り、それを使用してミドルウェアを適用する必要があるかどうかを判断します。 ヘッダー値は、区切り文字としてコロン (:) を使用してリストに分割され、リストに middlewareInfo.name 値が含まれているかどうかがチェックされます。 つまり、正しい値を持つx-middleware-subrequestヘッダーをリクエストに追加すると、ミドルウェアは、その目的に関係なく完全に無視され、NextResponse.next()を介して転送され、元の宛先へのパスはミドルウェアの影響を受けずに完了します。 このヘッドとその値は、すべてのルールをバイパスする「マスターキー」のようなものです。 この時点で、驚くべき問題が見つかったことに気付き、次にパズルの最後のいくつかのピースを完成させる必要があります。
私たちの「万能鍵」を機能させるためには、その値にmiddlewareInfo.nameが含まれている必要がありますが、この値は一体何なのでしょうか?
実行順序は middlewareInfo.name と同じです
middlewareInfo.name の値は非常に推測しやすく、それは単にミドルウェアのあるパスです。これを理解するためには、旧バージョンでのミドルウェアの設定方法について簡単に理解する必要があります。
まず、12.2バージョン以前——このバージョンではミドルウェアの規約が変更されました——ファイルは_middleware.tsという名前でなければなりません。また、appルーターはNext.jsのバージョン13でのみ導入されました。当時存在していた唯一のルーターはpagesルーターであったため、そのファイルはpagesフォルダー内に配置される必要があります(ルーターに特有)。
これらの情報を得ることで、ミドルウェアの正確なパスを推測し、x-middleware-subrequest ヘッダーの値を想定することができます。この値は、ディレクトリ名(つまり、その時点で存在する唯一のルーター名)とファイル名で構成され、当時の命名規則に従ってアンダースコアで始まります:
x-middleware-subrequest: pages/_middleware
私たちが、/dashboard/team/admin へのアクセス試行を /dashboard にリダイレクトするようにシステム的に構成されているミドルウェアを回避しようとしたとき:
成功しました、私たちは侵入しました ⚔️
私たちは今、中間層を完全に回避できるため、それに基づく保護システム、最も典型的なのが権限付与を回避できます。これは上記の例のように。この発見は非常に驚くべきものですが、考慮すべき他の点もあります。
12.2以前のバージョンでは、ネストされたルーティングがディレクトリツリーの任意の位置(pagesフォルダーから始まる)に1つ以上の_middlewareファイルを配置でき、それらには実行順序があり、Web Archiveから取得した古い文書のスクリーンショットで見られるようになっています:
これが私たちの脆弱性の悪用に何を意味するのか?
可能性 = パス内のレベルの数
したがって、/dashboard/panel/admin (ミドルウェアで保護されている) にアクセスするには、middlewareInfo.name の値と、それに応じて x-middleware-subrequest の値に 3 つの可能性があります。
pages/_ミドルウェア
または
pages/dashboard/_middleware
または
pages/dashboard/panel/_middleware
授権の神器:昨日は詩となり、今朝は更に価値がある
これまでのところ、私たちはバージョン13以前のバージョンのみが攻撃に対して脆弱であると考えています。なぜなら、ミドルウェアがソースコード内で移動されており、私たちはまだそのすべての側面をカバーしていないからです。私たちは、メンテナーがこの脆弱性に気づき、バージョン13の重要な変更の前に修正したに違いないと推測しています。そのため、私たちはフレームワークのメンテナーにこの脆弱性を報告し、研究を続けました。
驚くべきことに、最初の発見から2日後、すべてのバージョンのNext.js—バージョン11.1.4から—に脆弱性が存在することが分かりました! コードはもはや同じ場所になく、脆弱性の利用ロジックもわずかに変化しています。
前述したように、バージョン 12.2 以降、ファイルにはアンダースコアが含まれなくなり、単に middleware.ts という名前にする必要があります。 さらに、Pagesフォルダーには配置されなくなりました(バージョン13以降、アプリルーターが導入され、可能性が2倍になったため、これは私たちにとって便利です)。
それを考慮に入れると、バージョン12.2から始まる最初のバージョンのペイロードは非常にシンプルです:
この点を考慮して、バージョン 12.2 からの最初のリリースのペイロードは非常にシンプルです:
x-middleware-subrequest: ミドルウェア
/src ディレクトリ
Next.js が /src ディレクトリを作成する可能性を考慮する必要があります:
(Next.jsのドキュメント) プロジェクトのルートディレクトリに特別なNext.jsアプリやpagesディレクトリを持つ代替手段として、Next.jsはアプリケーションコードをsrcディレクトリに置く一般的なパターンもサポートしています。(Next.jsドキュメント)
この場合、ペイロードは次のようになります:
x-middleware-subrequest: src/middleware
したがって、パスにいくつのレベルがあっても、全体で2つの可能性しかありません。これにより、関連するバージョンの脆弱性を利用する難易度が簡素化されました。
最新バージョンでは、少し変化がありました(最後の変更を保証します)。
最大再帰深度
更新されたバージョンでは、ロジックが少し変更されました。このコードを見てください:
v15.1.7
以前と同様に、システムは x-middleware-subrequest ヘッダーの値を検索し、コロンを区切りとしてリストを形成します。しかし今回は、リクエストを直接転送する条件、すなわちミドルウェアルールを無視する条件が異なります:
定数depthの値は、定数MAX_RECURSION_DEPTHの値(つまり5)以上でなければなりません。代入の過程で、リストsubrequests(つまり:で区切られたヘッダー値)の中のある値がparams.name(つまりミドルウェアのパス)と等しい場合、定数depthは1増加します。前述のように、ここには2つの可能性しかありません:middlewareまたはsrc/middleware。
したがって、ミドルウェアを回避するために、リクエストに以下のヘッダー/値を追加するだけです:
x-middleware-subrequest: ミドルウェア:ミドルウェア:ミドルウェア:ミドルウェア:ミドルウェア
又は
x-middleware-subrequest: src/middleware:src/middleware:src/middleware:src/middleware:src/middleware
このコードは最初に何をするために使われていたのですか?
このコードは、再帰的なリクエストが無限ループに陥るのを防ぐためのもののようです。
悪用
あなたがこのようなコンテンツを好むことが分かったので、Bug Bounty Programからの実際のケースをいくつか紹介します。
承認/オーバーライドのバイパス
この例では、/admin/loginにアクセスしようとしたときに、404レスポンスを受け取りました。レスポンスヘッダーから、中間ウェアが認可されていないまたは不適切なユーザーのアクセスを防ぐためにパスの書き換えを実行したことがわかります。
しかし、私たちの認可された神器を使用すると:
私たちはこのエンドポイントに障害なくアクセスでき、中間ウェアは完全に無視されます。目標 Next.js バージョン:15.1.7
CSPを回避する
今回のサイトでは、ミドルウェアを使用して設定します——他の機能の他に——CSPとクッキーを:
それを回避しましょう:
ターゲット next.js バージョン:15.0.3
注意:2つのターゲットのペイロードの違いに注意してください。一方はsrc/ディレクトリを使用しており、もう一方は使用していません。
キャッシュポイズニングによるDoSの実現(何?)
はい、この脆弱性を通じてキャッシュポイズニングDoS攻撃を実現することも可能です。これは明らかに私たちが最初に探すべきことではありませんが、敏感なパスが保護されておらず、より興味深い悪用ポイントがない場合、特定の状況ではキャッシュポイズニングサービス拒否(CPDoS)を引き起こす可能性があります。
あるウェブサイトがユーザーの地理的位置に基づいてユーザーパスをリライトし、/en、/frなどを追加し、かつルートパス(/)上にページやリソースを提供していないと仮定します。ミドルウェアをバイパスすると、リライトを回避し、最終的にルートページに到達します。開発者はユーザーがルートページにアクセスできるようにするつもりはなかったため、対応するページが提供されておらず、404(またはリライトの設定/タイプによっては500)が得られることになります。
もしそのウェブサイトがキャッシュ/CDNシステムを使用している場合、404レスポンスを強制的にキャッシュする可能性があり、ページが利用できなくなり、サイトの可用性に深刻な影響を与えることがあります。
闡
安全公告が発表されて以来、私たちは一部の人々からの問い合わせを受けました。彼らは自分のアプリケーションの安全性について心配しており、攻撃の範囲をあまり理解していません。明確にしておく必要があるのは、攻撃を受けやすい要素はミドルウェアです。もしミドルウェアを使用していない(または少なくとも敏感な目的には使用していない)のであれば、心配する必要はありません(ただし、上記で言及されたDoSの点を確認してください)。なぜなら、ミドルウェアを回避しても、実際のセキュリティメカニズムは回避できないからです。
そうしないと、結果は壊滅的なものになる可能性があるため、安全通知に記載された指導策を迅速に実施することをお勧めします。
セキュリティに関するお知らせ - CVE-2025-29927
パッチ
Next.js 15.xについて、この問題は15.2.3で修正されました。
Next.js 14.x に関しては、この問題は 14.2.25 で修正されました
Next.js バージョン 11.1.4 から 13.5.6 については、以下の解決策を参照することをお勧めします。
ソリューション
安全なバージョンにアップグレードできない場合は、x-middleware-subrequest リクエストヘッダーを含む外部ユーザーのリクエストがあなたの Next.js アプリにアクセスするのをブロックすることをお勧めします。
過酷
CVSS: 3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N (深刻度: 9.1/10、緊急)
もっと情報
この記事を執筆している時点で、VercelとNetlifyに展開されているアプリは明らかにこの脆弱性の影響を受けていない(更新:誤報が多数存在するため、Cloudflareはこのルールをユーザーが手動で有効にした場合のみ有効になるように調整した——これらの誤報は、正当なユーザーからのリクエストと潜在的な攻撃者からのリクエストを効果的に区別できなかった)。
免責事項
本研究の公開は教育目的のみに使用され、開発者が問題の根本的な原因を理解する手助けをすること、または研究者/脆弱性ハンターが将来の研究作業においてインスピレーションを得ることを目的としています。本稿はセキュリティ公告の補足資料として、脆弱性の本質に関するさらなる説明と明確化を提供します——公告で脆弱性を引き起こすリクエストヘッダー(および関連する提出差異)が公開されているためです。
私たちは、本記事のいかなる不道徳な使用も支持しないことを明確に声明します。
エピローグ
この記事で強調したように、この脆弱性は数年前からNext.jsソースコードに存在しており、ミドルウェアとそのバージョンが進化するにつれて変化しています。 深刻な脆弱性はどのソフトウェアにも発生する可能性がありますが、最も一般的なフレームワークの1つに影響を与えると特に危険になり、より広範なエコシステムに深刻な影響を及ぼします。 前述したように、この記事を書いている時点で、Next.jsは週に1,000万回近くダウンロードされています。 これは、銀行サービスからブロックチェーンに至るまで、主要な分野で広く使用されています。 脆弱性が、承認や認証など、ユーザーが依存する成熟した機能に影響を与える場合、リスクはさらに大きくなります。
Vercelチームはこの脆弱性を解決するのに数日間かかりましたが、注目すべきは、彼らが問題に気付くとすぐに修正が提出され、数時間以内に新しいバージョンに統合されたことです(後方互換性も含む)。
タイムライン :
2025年2月27日:メンテナーに脆弱性を報告しました(当時、12.0.0から12.0.7バージョンのみが影響を受けると考えており、その点を報告に明記しました)。
2025年3月1日:2通目のメールを送信し、実際にはすべてのバージョンに脆弱性が存在すること、最新の安定版も含まれていることを説明しました。
2025年3月5日:Vercelチームからの初回の返信を受け取り、12.xバージョンはもはやメンテナンスされていないことが示されました(おそらく、私たちが2通目のメールに添付したセキュリティ通知テンプレートを読んでいないため、すべてのバージョンが影響を受けていることに気づいていない)。
2025年3月5日:再度メールを送信しました。チームはできるだけ早く2通目のメールとセキュリティ公告テンプレートを確認してください。
2025 年 3 月 11 日: 新しい情報が受け入れられたことを確認するために、別のメールを送信します。
2025年3月17日:Vercelチームからの返信を受け取り、関連情報が採用されたことを確認しました。
2025年3月18日:Vercelチームからのメールを受け取り、報告が受理され、修正パッチが完成したことが伝えられました。数時間後、修正を含むバージョン15.2.3(およびバックポート修正を含む)がリリースされました。
2025年3月21日:セキュリティ通知が正式に発表されました。
全体的に見て、ゼロデイ脆弱性を探すプロセスは、手がかりを見つけた瞬間だけが刺激的でアドレナリンが上昇する。それ以外の時間は、不確実性に満ちた旅のようなものだ。好奇心旺盛な人にとっては、知識の獲得につながるが、忍耐力に欠ける人にとっては、この旅は特に長く感じられる。ためらわず、チームを組んで行動することは、一人で砂漠を渡るよりもはるかに楽だ。