CVE-2025-29927は何を語るのか

ウェブアプリケーションのセキュリティにおいて、認可メカニズムはまさに最後の砦です。適切に実装されていれば、不正なアクセスから大切なリソースを守ることができます。しかし、その砦に予期せぬ抜け穴が存在した場合、その影響は計り知れません。

オープンソースのウェブ開発フレームワークである「Next.js」に、重大な脆弱(ぜいじゃく)性が存在したことが発見されました。この脆弱性を使用すれば攻撃者が認証チェックを回避することが可能であったことが明らかになっています。

本稿では、CVE-2025-29927 の技術的な詳細と悪用シナリオを解説するとともに、Next.js がこのヘッダーを導入した背景を考察します。そして、この脆弱性から私たちが学ぶべき設計原則とは何かを探り、今後のセキュアなウェブアプリケーション開発に向けての教訓としたいと思います。

CVE-2025-29927 の技術的詳細と悪用シナリオ

CVE-2025-29927 は、Next.js のバージョン 11.1.4 より後、かつ 12.3.5、13.5.9、14.2.25、15.2.3 より前のバージョンに存在する、ミドルウェアにおける認可バイパス の脆弱性です。

技術的詳細

この脆弱性の根本的な原因は、Next.js のミドルウェアにおけるリクエスト処理の不備にあります。本来、x-middleware-subrequest ヘッダーは Next.js 内部で行われるサブクエスト(例えば、サーバーコンポーネントからのデータフェッチなど)を識別するために使用されます。しかし、脆弱なバージョンの Next.js では、外部からの悪意のあるリクエストにこの x-middleware-subrequest ヘッダーが含まれていた場合、ミドルウェアに実装された認可チェックが意図せずスキップされてしまう 可能性があります。

ミドルウェアは、リクエストがアプリケーションのルートハンドラーに到達する前に実行されるため、認証や認可といったセキュリティに関わる重要な処理を担います。この認可チェックがバイパスされることで、本来アクセスを拒否されるべきユーザーが、保護されたリソースに不正にアクセスすることが可能になります。

悪用シナリオ

攻撃者は、この脆弱性を悪用するために、以下のような手順を踏む可能性があります。

  1. ターゲットとなる Next.js アプリケーションのバージョンを特定します。 脆弱なバージョンを使用しているアプリケーションが攻撃対象となります。
  2. 保護されたリソースへのアクセスを試みます。 通常であれば認可が必要なページや API エンドポイントに対してリクエストを送信します。
  3. リクエストの HTTP ヘッダーに x-middleware-subrequest (真と評価される値)を追加します。
  4. 脆弱なアプリケーションは、このヘッダーの存在を内部リクエストと誤認し、本来行うべき認可チェックをスキップしてしまう可能性があります。
  5. その結果、攻撃者は本来アクセス権限のないリソースに不正にアクセスしたり、機密情報を窃取したり、予期せぬ操作を実行したりする可能性があります。

例えば、管理権限が必要なページへのアクセス制限が、この脆弱性を悪用することで一般ユーザーにバイパスされ、重要な設定が不正に操作されるといったシナリオが考えられます。

コードリーディング

v13.2.8の場合https://github.com/vercel/next.js/blob/v13.5.8/packages/next/src/server/web/sandbox/sandbox.ts#L75-L88

export const run = withTaggedErrors(async function runWithTaggedErrors(params) {
  const runtime = await getRuntimeContext(params)
  const subreq = params.request.headers[`x-middleware-subrequest`]
  const subrequests = typeof subreq === 'string' ? subreq.split(':') : []
  if (subrequests.includes(params.name)) {
    return {
      waitUntil: Promise.resolve(),
      response: new runtime.context.Response(null, {
        headers: {
          'x-middleware-next': '1',
        },
      }),
    }
  }

ざっくり読んでいくと、x-middleware-subrequest の値を取得し区切り文字(:)でスプリットし、params.nameが含まれているかをチェックします。つまり、正しい値を持つ x-middleware-subrequest ヘッダーをリクエストに追加すると、ミドルウェアは(その目的に関わらず)完全に無視され、リクエストは NextResponse.next() を介して転送され、ミドルウェアの影響を受けることなく元の宛先まで到達します。params.namemiddleware もしくは src/middleware をとるので以下のようなヘッダを追加することで攻撃が成功することになります。

x-middleware-subrequest: middleware

or

x-middleware-subrequest: src/middleware

v15.2.2の場合
https://github.com/vercel/next.js/blob/v15.2.2/packages/next/src/server/web/sandbox/sandbox.ts#L94-L114

export const run = withTaggedErrors(async function runWithTaggedErrors(params) {
  const runtime = await getRuntimeContext(params)
  const subreq = params.request.headers[`x-middleware-subrequest`]
  const subrequests = typeof subreq === 'string' ? subreq.split(':') : []

  const MAX_RECURSION_DEPTH = 5
  const depth = subrequests.reduce(
    (acc, curr) => (curr === params.name ? acc + 1 : acc),
    0
  )

  if (depth >= MAX_RECURSION_DEPTH) {
    return {
      waitUntil: Promise.resolve(),
      response: new runtime.context.Response(null, {
        headers: {
          'x-middleware-next': '1',
        },
      }),
    }
  }

v14以降は多少変更されておりミドルウェアの最大深度(MAX_RECURSION_DEPTH)を確認するようになっています。そのため以下のようなヘッダを追加することになります。(最大深度5以上にする必要がある)

x-middleware-subrequest: middleware:middleware:middleware:middleware:middleware

or

x-middleware-subrequest:src/middleware:src/middleware:src/middleware:src/middleware:src/middleware

x-middleware-subrequest の本来の意図と Next.js の設計思想

CVE-2025-29927 の核心にある x-middleware-subrequest ヘッダーは、Next.js がミドルウェア内で内部的なリクエストを効率的に処理するために導入された仕組みと考えられます。Next.js は、サーバーサイドレンダリング (SSR)、サーバーコンポーネント、API ルートなど、多様なバックエンド処理を React のエコシステム内で実現するためのフレームワークです。これらの機能を円滑に動作させるためには、リクエストのライフサイクル全体をミドルウェアで制御しつつ、内部的な処理を最適化する必要があります。

x-middleware-subrequest ヘッダー導入の背景と目的(推測を含む):

  • 内部リクエストの識別: Next.js が内部的にリソースをフェッチする際(例:サーバーコンポーネントが API エンドポイントからデータを取得する、next/link によるプリフェッチなど)、これらのリクエストを外部からのユーザーリクエストと区別するために x-middleware-subrequest ヘッダーが用いられたと考えられます。
  • 処理の最適化: 内部的なリクエストは、外部からのリクエストとは異なる処理を必要とする場合があります。例えば、認証や認可のチェックを省略したり、キャッシュ戦略を調整したりといった最適化が考えられます。x-middleware-subrequest ヘッダーはそのような条件分岐をミドルウェア内で実現するために役立った可能性があります。
  • 柔軟なミドルウェアロジックの実現: 内部リクエストと外部リクエストを区別することで、開発者はより複雑で柔軟なミドルウェアのロジックを構築できます。例えば、特定の内部リクエストに対してのみ特定のヘッダーを付与したり、ログを記録したりといった処理が考えられます。

Next.js の設計思想との関連性(推測を含む):

Next.js は、「開発体験の向上」と「プロダクション環境での高いパフォーマンス」を両立させることを重視しているフレームワークです。ミドルウェアは、その両方の目標を達成するための重要な機能の一つと言えるでしょう。

  • 開発体験の向上: ミドルウェアは、ルーティングの前処理を一箇所に集約し、認証、認可、リダイレクト、ヘッダー操作などを簡潔に記述できるため、開発者はアプリケーション全体のロジックを把握しやすくなります。x-middleware-subrequest ヘッダーは、このようなミドルウェアの柔軟性をさらに高めるための仕組みとして導入された可能性があります。
  • プロダクション環境での高いパフォーマンス: 内部リクエストと外部リクエストを区別し、それぞれに最適化された処理を行うことで、アプリケーション全体のパフォーマンス向上に貢献することが期待されます。例えば、不要な認証処理を内部リクエストに対して省略することで、処理時間を短縮できる可能性があります。

重要なポイント:セキュリティとのトレードオフ

x-middleware-subrequest ヘッダーは、Next.js の内部処理を効率化し、開発者に柔軟性を提供する意図があったと考えられますが、今回の CVE-2025-29927 の事例が示すように、その取り扱いを誤るとセキュリティ上の脆弱性に繋がる可能性があります。内部的な仕組みに依存した最適化を行う際には、セキュリティ上の影響を十分に考慮し、信頼の境界を明確にすることが極めて重要です。

Next.js チームは、この脆弱性の発覚を受けて迅速に対応し、修正版をリリースしています。これは、Next.js がセキュリティを重要な懸念事項として捉え、コミュニティからのフィードバックを真摯に受け止めていることの表れと言えるでしょう。

CVE-2025-29927 から学ぶ設計原則

CVE-2025-29927 のように、一見すると内部的な最適化を目的とした仕組みが、セキュリティ上の脆弱性に繋がってしまう事例は、ウェブアプリケーションの設計において重要な教訓を与えてくれます。この脆弱性から学ぶべき主要な設計原則を以下に示します。

  • 信頼の境界を明確にする: 内部からのリクエストと外部からのリクエストを区別することは重要ですが、内部的なリクエストであっても、安易に信頼してセキュリティチェックを省略すべきではありません。信頼の境界を曖昧にすると、今回のように外部からの悪意のある操作によって内部的な処理フローが誤って解釈され、脆弱性に繋がる可能性があります。すべての入力は、その出所に関わらず検証することを原則とすべきです。
  • 入力の検証と制御を徹底する: HTTP ヘッダーもユーザーからの入力の一部です。x-middleware-subrequest ヘッダーの存在を条件に処理を分岐させる場合、そのヘッダーが外部から意図的に操作される可能性を考慮する必要があります。予期しない値や形式のヘッダーを受け取った場合の処理を明確に定義し、不正な値は拒否するように設計すべきです。
  • 責務の分離を意識する: 認可というセキュリティ上の重要な責務と、内部処理の最適化という責務を、同じコードブロック内で混在させるべきではありません。認可ロジックは独立したモジュールや関数として実装し、内部処理の最適化ロジックと明確に分離することで、コードの可読性と保守性を高め、セキュリティ上のミスを減らすことができます。
  • 防御の多層性 (Defense in Depth) を採用する: 単一のセキュリティ対策に依存するのではなく、複数の防御層を設けることが重要です。今回のケースでは、ミドルウェアでの認可チェックだけでなく、アプリケーションのルートハンドラーやデータアクセス層でも追加のセキュリティ対策を施していれば、脆弱性の影響を軽減できた可能性があります。
  • 設計の透明性と理解可能性を高める: 内部的な仕組みが複雑化すると、セキュリティ上の盲点が生じやすくなります。設計はシンプルで理解しやすく、意図しない動作や副作用がないように心がけるべきです。特に、セキュリティに関わる部分は、複数の開発者やレビュー担当者が容易に理解し、検証できることが重要です。
  • セキュリティバイデザイン (Security by Design) の原則: 開発の初期段階からセキュリティを考慮した設計を行うことが不可欠です。機能要件だけでなく、潜在的なセキュリティリスクを洗い出し、それを軽減するための対策を設計に組み込むことで、後から脆弱性が発覚するリスクを減らすことができます。x-middleware-subrequest のような内部的な仕組みを導入する際にも、セキュリティチームのレビューを受けるなど、早期にセキュリティの専門家の意見を取り入れるべきです。

まとめ:CVE-2025-29927 が語る教訓

Next.js のミドルウェアにおける認可バイパス脆弱性 CVE-2025-29927 は、一見すると些細な内部処理の最適化のためのヘッダーが、セキュリティの根幹を揺るがす事態を引き起こし得ることを示しました。この事例は、現代のウェブアプリケーション設計において、私たちが決して忘れてはならない重要な教訓をいくつも提示しています。

まず、内部と外部の境界線を曖昧にすることの危険性です。x-middleware-subrequest ヘッダーは内部的な通信を効率化するために導入されたと考えられますが、外部からの制御を許してしまった結果、信頼の境界が崩れ、認可バイパスという深刻な脆弱性を生み出しました。いかなる入力も信頼せず、常に検証するという原則の重要性を改めて認識する必要があります。

次に、セキュリティは後付けではなく、設計の初期段階から考慮すべき最優先事項であるということです。機能実装の効率性やパフォーマンスの最適化も重要ですが、セキュリティを軽視した設計は、最終的に大きな代償を払うことになります。セキュリティバイデザインの原則に従い、開発ライフサイクルの早い段階からセキュリティ専門家のレビューを受けるなど、積極的にセキュリティを組み込む姿勢が求められます。

また、防御の多層性の重要性も再認識させられました。単一の防御策(この場合はミドルウェアの認可チェック)に依存するのではなく、複数のレイヤーでセキュリティ対策を講じることで、一つの脆弱性が全体のセキュリティを破綻させるリスクを低減できます。

最後に、フレームワークの内部動作を深く理解することの重要性です。x-middleware-subrequest ヘッダーの意図された使い方と潜在的なセキュリティリスクを理解していれば、今回の脆弱性の発生を防ぐことができたかもしれません。開発者は、利用する技術の特性を深く理解し、その上で安全な設計を行う責任があります。

CVE-2025-29927 は、Next.js を利用する開発者だけでなく、広くウェブアプリケーション開発に携わる全ての人々にとって、設計のあり方、セキュリティとの向き合い方を再考する貴重な機会を与えてくれました。この教訓を活かし、より堅牢で安全なウェブアプリケーションの開発を目指していく必要があるでしょう。