← ブログ一覧

Next.js CVE9件、脆弱版とパッチ版を実機比較

Next.jsが2026年7月に一斉パッチした9件のCVEのうち、認証不要で突けるミドルウェアバイパスの脆弱版・パッチ版をサンドボックスで実際に動かし、再現の成否まで含めて検証した記録。


この記事の結論: Next.jsが2026年7月に一斉パッチした9件のCVEのうち、認証不要でリモートから突けるミドルウェアバイパス(CVSS 8.3)を脆弱版・パッチ版の両方で実際に動かして検証した。バイパス自体の再現はできなかったが、公開PoCが無いCVEにどこまで検証コストをかけるべきかの線引きと、検証過程で見つかった別の非互換動作を持ち帰りとして書く。

リナ社長(高校生社長のやり手ギャル)× タクヤ(入社3年目)— 今回は、Next.jsの一斉パッチ発表を受けて、脆弱版と修正版を実際に手元で動かしてバイパスを再現できるか試した回。(登場人物について

「タクヤ、Next.jsが9件もCVE一気にパッチしたんだって。しかもCVSS 8.3のやつは認証いらずでミドルウェア突破できるとか書いてあるじゃん。ウチはAstroだから関係ないっちゃないけど、これ数字だけ見て終わらせていい話?」

「いや、CVSS高いなりに条件がかなり限定的なんですよ。Turbopackでビルドして、App Routerで、i18n.localesを1個だけ設定してる場合、らしくて。うちには関係なさそうですけど、条件が細かいCVEって逆に『本当にそうなるのか』を確かめないと納得できないタイプなんですよね」

「じゃあ手を動かそっか。脆弱版と直った版、両方入れて比べてみよ」


何が起きたか:事前告知型の一斉パッチと2つの主要CVE

2026年7月21日、Next.jsは「事前告知型セキュリティリリース」という方式で、9件のCVE(High 4件・Medium 5件)を一斉にパッチした。修正版はActive LTS系のv16.2.11と、Maintenance LTS系のv15.5.21。単発のパッチではなく複数件をまとめて事前告知した上でリリースするのは運用スタイルの変化として押さえておきたいポイントだ。

9件のうち、実利インパクトが大きいのは以下の2つ。

CVE深刻度内容影響バージョン修正版
CVE-2026-64642(GHSA-6gpp-xcg3-4w24)High(CVSS 8.3)App Router + Turbopackビルド + i18n.localesを1件だけ設定時、細工したリクエストでミドルウェアの認可チェックをバイパス可能16.0.0〜16.2.1016.2.11
CVE-2026-64645High(CVSS 8.3)rewrites()/redirects()でリクエスト由来の入力から外部ホスト名を組み立てている場合、任意ホストへ誘導可能(rewritesはSSRF、redirectsはオープンリダイレクト)16.2.11 / 15.5.21

CVE-2026-64642は、Webpackビルドや新しいproxy.ts規約(middleware.tsの後継として案内されている)は対象外とされている。攻撃には認証が不要でリモートから実行可能とされる一方、GitHub Security Advisory(GHSA-6gpp-xcg3-4w24)を直接確認した限り、具体的な再現手順や公開PoCの記載は無い。Next.js公式ブログとNetlifyの解説記事も内容としては同様の説明をしているが、この2件は直接アクセスが403で拒否されたため、検索結果の要約から内容を突き合わせている。3つの一次ソースの記述に矛盾は無いが、技術的な再現詳細はどこにも出ていない、という点が今回の検証の出発点になる。


タクヤ視点:条件の細かさとproxy.tsへの移行

「条件を並べると『App Router』『Turbopackビルド』『i18n.localesが1個だけ』『middleware.ts規約』の4つが同時に揃わないといけないんですよね。どれか1つ外れれば対象外です」

「1個だけロケール設定してるプロジェクトって逆に珍しくない?複数言語対応するために使う機能なのに、1個しか設定してないってなんか変じゃん」

「そこなんです。i18nルーティングを将来のために仕込んどいて実質1言語のまま運用してる、みたいなケースは意外とありそうで。あとmiddleware.ts規約自体がNext.js側で非推奨扱いになってて、proxy.tsへの移行が案内されてるので、対象になるのは『まだ移行してない古い構成』が中心になりそうです」

条件が細かいCVEは影響範囲が狭い分、該当する現場では見落とされやすい。特に「i18nを将来用に1つだけ設定したまま放置」というのは、機能追加の途中でよくある状態であり、意図的に脆弱な設定を作っているわけではない点が厄介だ。


リナ社長視点:うちは対象外、でも「関係ない」で終わらせていいのか

「で、jinbei-labはAstro + Cloudflare Pagesだから、このCVEそのものはウチには刺さらないんだよね。でもさ、それって『他人事だから流し読みでOK』ってこと?」

「ビジネス的には無関係と言い切っていいと思います。CVSSが高いって言っても、条件に当てはまらないなら、jinbei-labとしてはこの件はここで終わりでいいんじゃないですかね。うちのスタックに無い話を深追いする理由は薄いというか」

「いや、それはちょっと違わない?対象外だからこそ、事前告知型リリースに切り替えたっていう運用スタイルの変化とか、CVSS 8.3っていう深刻度そのものは知っておくべきだと思うんだよね。関係ないからスルーしていい話じゃなくない?」

ここで評価が一度ぶつかる。タクヤは技術条件の狭さを理由に「対象外なら深追いする必要はない」という切り捨て方向の判断をしたが、リナ社長は「対象外であっても運用スタイルの変化や深刻度そのものは学んでおくべき」と反論した。結局は、「対象の有無を自分で確かめる」という一点で着地する。

「で、それってウチら読者の何が変わるの?って話だけど。CVE番号とパッチバージョンだけ見て『対応した』で終わらせず、自分のスタックに条件が当てはまるか実際に確認する、って習慣がないと、次に来る別のCVEでも同じことを繰り返すじゃん」


実機検証:脆弱版と修正版を実際に動かしてみた

ここからは実際にサンドボックスで行った検証だ。Node v22.22.2、npm 10.9.7の環境で、隔離ディレクトリを2つ用意し、それぞれにnext@16.2.10(脆弱版)とnext@16.2.11(パッチ版)を実際にnpm installした。

$ npm ls next   # 脆弱版側
next@16.2.10

$ npm ls next   # パッチ版側
next@16.2.11

最小のApp Routerアプリを作成し、middleware.tsx-secret-token: let-me-inヘッダーが無い場合は/blockedにリダイレクトする単純な認可チェックを実装、app/protected/page.tsxを保護対象ページとした。next.config.jsにはCVEの条件通り単一ロケールのi18n設定を入れた。

// next.config.js
module.exports = {
  i18n: { locales: ['en'], defaultLocale: 'en' },
}

まずnpx next build --turbopackで本番ビルドを試みたところ、脆弱版・パッチ版の両方で同一のビルド時クラッシュに遭遇した

⚠ i18n configuration in next.config.js is unsupported in App Router.
...
Error [PageNotFoundError]: Cannot find module for page: /_document

i18n設定はApp Routerでは「unsupported」と警告が出るにもかかわらず、pages/_document.tsxのスタブを追加しても同じエラーで解消しなかった。設定が完全に無視されているわけではなさそうだが、内部で何が起きているかまでは特定できていない。このクラッシュは16.2.10・16.2.11のどちらでも同一に発生しており、CVEの修正差分とは無関係の、レガシーi18n設定とApp Routerの組み合わせで起きる別の非互換動作だと判断した。結果、CVE-2026-64642が要求する「Turbopackでの本番ビルド」条件をこのサンドボックス環境内では完成させられなかった。

そこで代替として、脆弱版(16.2.10)でnpx next dev --turbopack(開発サーバー、Turbopack使用)を起動し、/protectedに対してトークン無しで複数パターンのリクエストを送った。

/protected(トークンなし)             → 307 redirect to /blocked
/protected(正しいトークン付き)        → 200 OK
/en/protected(ロケールプレフィックス)  → 307 redirect to /blocked
//protected(二重スラッシュ)           → 308 redirect to /protected
/protected/(末尾スラッシュ)           → 308 redirect
/./protected(ドットセグメント)        → 307 redirect to /blocked
/%2e/protected(パーセントエンコード)   → 307 redirect to /blocked

いずれのパターンでもミドルウェアのバイパスは観測されなかった。

「結局バイパスできなかったってことですよね」

「うん、正直に言うとね。公開PoCが無いから、具体的にどんなリクエストが通るのかまでは再現できなかった。それに本番Turbopackビルド自体が別のクラッシュで完走しなかったから、CVEが要求する条件そのものを完全には再現できてない」

「でも収穫はゼロじゃないですよね。i18n設定がApp Routerで『unsupported』って警告されるのに、スタブ足しても解消しないのは、設定がどこかでまだ引きずられてる感じがしますね。これ、CVEの根本原因と同じ構造だと思うんです。『レガシー設定が新しいルーターに中途半端に染み出す』っていう」

「それめっちゃ良い指摘。バイパスの再現はできなかったけど、別の不整合を実際のコマンドで見つけられたのは、公式発表を読むだけじゃ絶対出てこない情報だよね」


まとめ・持ち帰り

Next.jsの一斉パッチのうち、CVE-2026-64642は認証不要・リモートで突けるミドルウェアバイパスとして深刻度が高い一方、公開されているPoCは見つからず、条件も「App Router + Turbopackビルド + 単一ロケールi18n + middleware.ts規約」という限定的なものだった。実際にサンドボックスで脆弱版・パッチ版を動かして再現を試みたが、本番Turbopackビルド自体が別の非互換クラッシュで完走せず、devサーバーでの基本的なリクエストバリエーションでもバイパスは確認できなかった。この「うまくいかなかった」という結果自体が、今回の検証の正直な記録だ。

持ち帰りとして具体的な判断基準を1つ示す。公開PoCが無いCVEは、パッチ適用は最優先で行いつつ、独自の再現検証に時間をかけすぎない、という線引きだ。今回のように条件を揃えるだけで別の非互換問題に阻まれるケースもあり、再現に固執するコストは費用対効果が悪い。パッチバージョンを上げることと、再現検証の深追いは切り分けて考えるべきだ。

リナ社長からひとつ、読者への問い。「自分のプロジェクトで使ってるi18n設定とかミドルウェアの規約って、今どのバージョンのドキュメントに沿ってる?半年前の設定のまま放置してない?」


参考

コメント