本文へ移動
技術を、自社の仕事に。
判断と実行を助けるメディア

記事を検索

OpenNext Cloudflareの認証すり抜け 1.20.2未満で当たる条件

目次 · 6項目

/admin を開くと 401 が返る。ところが /%61dmin と打つと、管理画面の中身がそのまま表示される。%61 は小文字の「a」をパーセントエンコードしたもので、ブラウザーやサーバーによっては /admin と同じ場所を指します。Next.js の middleware で /admin 配下の認可をしているサイトを、Cloudflare Workers 向けのアダプター @opennextjs/cloudflare で配信している場合、1.20.2 未満の版ではこれが起こり得ます。

2026年10月15日(UTC)、GitHub Advisory Database に GHSA-2vc4-c4r6-p6v6(CVE-2026-100409、重大度 HIGH、CVSS 3.1 で 7.5)が登録されました。対象は @opennextjs/cloudflare の 0.3.0 以上 1.20.2 未満です。修正版の 1.20.2 は2026年7月21日に npm に公開され、opennextjs/opennextjs-cloudflare のリポジトリのアドバイザリも同じ日に公開されていました。npm audit が参照する Advisory Database に入ったのはその3か月近く後です。7月以降に依存を更新していないサイトでは、気づかないまま残っている可能性があります。

本記事は、自社のサイトが当たるかを確かめる手順、編集部の検証環境で再現した結果、更新先の版、middleware だけに認可を任せない設計の見直しを整理したものです。

アドバイザリが挙げる3つの条件

アドバイザリは、次の3つがそろうアプリを対象としています。

  1. enableCacheInterception を明示的に有効にしている
  2. アクセス制御を middleware に頼っている
  3. 保護したいルートの応答がキャッシュに入っている

原因は、パスの扱いの食い違いです。アドバイザリによれば、middleware の照合はエンコードされたままのパス名を使い、キャッシュの横取り(cache interception)はパス名をデコードしてからキャッシュの項目を選んでいました。/%61dmin は matcher の /admin/:path* に一致しないため認可の middleware が動かず、続くキャッシュの検索では /admin として扱われて、保護したページのキャッシュが返ります。/%61dmin/%ZZ のように壊れたエンコードを含むパスでは、区切りごとにデコードしたため、一部だけデコードしたパスでキャッシュを探し続ける問題もあったとしています。

1. enableCacheInterceptionを有効にしているか

open-next.config.ts を開き、defineCloudflareConfig に enableCacheInterception: true があるかを見ます。1.20.2 の型定義では既定値が false です。

// open-next.config.ts(この設定があれば条件1に当たる)
export default defineCloudflareConfig({
  incrementalCache: staticAssetsIncrementalCache,
  enableCacheInterception: true,
});

既定で無効だからといって、設定していないとは限りません。OpenNext のドキュメントの原稿(opennextjs/docs の pages/cloudflare/caching.mdx)は、キャッシュの設定例として「Small site using revalidation」「Large site using revalidation」「SSG site」の3つを載せており、いずれも enableCacheInterception: true を含んでいます。ガイドの例をそのまま写して作ったサイトは、有効になっている可能性があります。

2. 認可をmiddlewareだけでしているか

middleware.ts の config.matcher と、その中で 401 やログイン画面への転送を返している処理を確かめます。そのうえで、保護したいページ(app/admin/page.tsx など)やルートハンドラーの側でも、セッションや権限を確かめているかを見ます。ページの側に確認がなく、middleware が唯一の関門なら、条件2に当たります。

3. 保護したいルートがキャッシュに入っているか

ビルド後の .next/prerender-manifest.json の routes を見ると、静的に生成されたルートが分かります。保護したいパスがここに並んでいれば、そのページはビルド時に生成されてキャッシュに入ります。ISR のルート(revalidate を指定したページ)も、一度生成されればキャッシュに入ります。

node -e "console.log(Object.keys(require('./.next/prerender-manifest.json').routes))"

あわせて、npm ls @opennextjs/cloudflare で版を確かめます。編集部の検証環境では、1.20.1 を入れた構成で npm audit(npm 10.9.4)を実行すると、GHSA-2vc4-c4r6-p6v6 が high として表示されました。

手元で再現した結果

2026年10月21日、編集部の検証環境(Linux、Node.js v22.22.0)で、アドバイザリの例に沿った最小の App Router のアプリを作りました。/admin は静的なページで本文に PROTECTED-ADMIN-CONTENT を表示し、middleware は matcher /admin/:path* に一致した要求に「Authentication required」と 401 を返します。キャッシュには、ドキュメントの「SSG site」と同じ staticAssetsIncrementalCache を使いました。opennextjs-cloudflare build と populateCache local の後、wrangler dev --local で起動して curl --path-as-is で要求しています。Cloudflare のアカウントへのデプロイはしていません。

最初の構成(/ と /admin だけ)では、1.20.1 でも /%61dmin は 404 になり、すり抜けは起きませんでした。調べると、OpenNext のルーティングが /%61dmin をどのページにも一致しないと判断し、キャッシュを探す前に 404 のページへ切り替えていました。そこで、ルート直下に動的なページ app/[slug]/page.tsx を足すと、結果が変わりました。

版(Next.js)enableCacheInterception/admin/%61dmin
1.20.1(16.2.11)true401200、x-opennext-cache: HIT、保護ページの本文
1.20.1(16.2.11)false401200、保護ページの本文
1.20.2(16.2.11)true401401
1.20.2(16.2.11)false401401
1.20.12(16.4.2)true401401

/%61dmin/%ZZ は、どの版でも 404 でした。各要求は2回ずつ送り、2回とも同じ結果です。

OpenNext Cloudflareで/%61dminを要求したときの処理の違いを示す図。1.20.1ではmiddlewareの照合がエンコードされたままのパスで行われて/admin/:path*に一致せず、続く処理でパスがデコードされて保護した/adminのページが200で返った。1.20.2ではmiddlewareがデコードしたパスでも照合するため一致し、401が返った。再現にはルート直下の動的なページが必要だった

キャッシュの機能を切るだけでは防げなかった

アドバイザリは、すぐに更新できない場合の回避策の1つ目に「enableCacheInterception: true を外す」ことを挙げています。しかし上の表のとおり、編集部の構成では、1.20.1 で enableCacheInterception を false にしても /%61dmin で保護ページの本文が返りました。このときはキャッシュの横取りの見出し(x-opennext-cache)が付かず、Next.js のサーバーがページを返しています。middleware の照合がエンコードされたパスのままで行われる点は、キャッシュの機能を切っても変わらないためと考えられます。

1.20.2 が依存する @opennextjs/aws 4.1.0 の変更履歴は、この修正(#1199)を「middleware とその照合を、デコードしたパス名でも試す。キャッシュの横取りは壊れたエンコードのパスを扱わない」と説明しています。パッケージの中身を比べても、4.1.0 では middleware の照合に decodeURIComponent した値の判定が加わり、キャッシュの横取りではデコードに失敗したら何もせずに次へ渡すよう変わっていました。

検証は1つの構成、手元の wrangler dev だけで行ったもので、本番の Workers で同じ結果になるかは確かめていません。それでも、ルート直下に [slug] や catch-all のような動的なページがあるサイトでは、キャッシュの設定を切っただけで安心せず、更新を先に進めることを編集部は勧めます。

修正は1.20.2、上げるなら1.20.11以上

この件の修正版は 1.20.2 で、10月21日時点の npm の latest は 10月15日公開の 1.20.12 です。@opennextjs/cloudflare は @opennextjs/aws の版を固定で指定しているため、本体を上げれば修正の入った @opennextjs/aws も入ります。

版(npm 公開日)同梱する @opennextjs/awsnext の peer の範囲wrangler の peer の範囲
1.20.1(2026-06-26)4.0.2>=15.5.18 <16 || >=16.2.6^4.86.0
1.20.2(2026-07-21)4.1.0>=15.5.21 <16 || >=16.2.11^4.86.0
1.20.11(2026-10-13)4.1.10>=15.5.27 <16 || >=16.3.8^4.148.0
1.20.12(2026-10-15)4.1.11>=15.5.28 <16 || >=16.4.1^4.148.0

1.20.2 にも peer の範囲の引き上げがあり、Next.js 16 なら 16.2.11 以上が必要です。1.20.11 は Next.js 16.3.8 以上(15系なら 15.5.27 以上)と wrangler 4.148.0 以上を、1.20.12 まで上げる場合は Next.js 16.4.1 以上(15系なら 15.5.28 以上)と、同じく wrangler 4.148.0 以上が求められます。Next.js 16.4.1 は Next.js 自体の10月の脆弱性の修正版でもあり、その内容はNext.js 10月の脆弱性9件の記事で整理しています。編集部は、Next.js と OpenNext をそろえて最新の版に上げ、ステージングで確かめてから本番に出す進め方を提案します。

1.20.2 には、ほかにも @opennextjs/aws 4.1.0 の変更が入っています。変更履歴によれば、トップページ(/)のキャッシュの読み出しの修正も含まれており、編集部の検証でも、1.20.1 では / に x-opennext-cache が付かず、1.20.2 では HIT が付きました。更新後にトップページの応答の見出しが変わっても、異常ではありません。

1.20.2で止めない:middlewareのヘッダー削除の件

1.20.2〜1.20.10 が同梱する @opennextjs/aws 4.1.0〜4.1.9 には、別の問題があります。opennextjs/opennextjs-aws のリポジトリが10月13日に公開したアドバイザリ GHSA-2mvv-9g7g-pfcg(重大度 high、CVSS 3.1 で 7.4、CVE なし)です。middleware が NextResponse.next() に渡す要求ヘッダーから利用者の送ったヘッダーを削除しても、OpenNext では削除したはずのヘッダーがアプリに届いていました。アドバイザリは、middleware で本人確認や権限のヘッダーを消し、ページやバックエンドが「そのヘッダーがないこと」を前提に判断しているアプリでは、認証や認可をすり抜けられる可能性があるとしています。middleware でヘッダーを削除していないアプリは対象外です。

対象は @opennextjs/aws 4.1.9 以下、修正版は 4.1.10 で、@opennextjs/cloudflare は 1.20.11 で同梱の版を 4.1.10 に上げました。修正は、今回の件と同じ middleware の処理のファイル(core/routing/middleware.js)に入っています。@opennextjs/cloudflare 側にはアドバイザリが出ておらず、10月21日の時点で Advisory Database にも入っていないため、npm audit には出ません。この件は編集部では検証していませんが、middleware でヘッダーを削除しているかどうかにかかわらず、今回の件の修正版の 1.20.2 で止めず、1.20.11 以上(実際には最新の 1.20.12)に上げることを勧めます。

なお、GHSA-2vc4-c4r6-p6v6 の対象に挙がっているのは @opennextjs/cloudflare だけです。AWS 向けに @opennextjs/aws を直接使う構成は対象に含まれておらず、npm audit にも出ません。修正そのものは @opennextjs/aws 4.1.0 に入っているため、AWS 向けの構成も、ヘッダーの件の修正を含む 4.1.10 以上かを確かめておくと安全です。

middlewareだけで守らない設計に直す

ここからは編集部の提案です。今回の問題は、「同じページを指すURLが1つではない」ことと、「認可を通り道の1か所だけで行っていた」ことが重なって起きました。版を上げたうえで、次の点も見直します。

保護したいページを静的に生成しない

認可が必要なページを静的に生成すると、その中身は認可と無関係にキャッシュに置かれます。今回の検証でも、/admin は静的なページだったため、キャッシュから本文がそのまま返りました。ログインした人だけが見る管理画面や会員ページは、要求ごとにサーバーで描画する(cookie やヘッダーを読む)作りにし、ページの側でもセッションを確かめます。

ページとルートハンドラーでも権限を確かめる

アドバイザリの回避策の2つ目も、「middleware だけに頼らず、保護したルートのハンドラーの中で認可を行う」ことです。Server Component、Server Action、Route Handler の入口でセッションを確かめる共通の関数を用意し、middleware は「未ログインならログイン画面へ送る」という利便のための関門と位置づけます。認証の仕組みを引き継いだときの確かめ方は、JWT検証ミドルウェアの落とし穴の記事でも扱っています。

エンコードしたパスを手前で拒否する

回避策の3つ目は、信頼できるプロキシで、保護したルートへのエンコードされたパスを Worker に届く前に拒否することです。アドバイザリは、matcher にエンコードした別名を足すだけでは不完全だとしています。同じパスを表すエンコードの仕方が複数あるためです。手前で拒否する場合も、更新までのつなぎとして扱います。

確認した範囲

2026年10月21日に、github/advisory-database の GHSA-2vc4-c4r6-p6v6 の JSON、opennextjs/opennextjs-cloudflare と opennextjs/opennextjs-aws の変更履歴(main)、npm に公開された @opennextjs/cloudflare 1.20.1・1.20.2 と @opennextjs/aws 4.0.2・4.1.0 のパッケージの中身、opennextjs/docs のキャッシュのガイドの原稿を確認しました(資料調査)。あわせて、最小の App Router のアプリを @opennextjs/cloudflare 1.20.1・1.20.2(Next.js 16.2.11、wrangler 4.86.0)と 1.20.12(Next.js 16.4.2、wrangler 4.151.3)でビルドし、wrangler dev --local で /admin・/%61dmin・/%61dmin/%ZZ への応答を比べ、1.20.1 で npm audit を実行しました(実機検証)。Cloudflare へのデプロイと本番の Workers での動作、R2 や Durable Objects を使うキャッシュの構成、Pages Router、ISR のルート、Next.js 15系、Next.js 16 の proxy.ts での動作、GHSA-2mvv-9g7g-pfcg の挙動と 1.20.11 の構成は確かめていません。公開前の10月21日には、GitHub の API で opennextjs/opennextjs-cloudflare と opennextjs/opennextjs-aws のリポジトリのアドバイザリ、Advisory Database の登録内容、NVD の CVE-2026-100409、npm の最新の公開記録、opennext.js.org のキャッシュのページも確認しました。

Cloudflare 上の Next.js のサイトの更新計画や、認可の作りの見直しは、開発・AI・自動化のご相談からお問い合わせください。Vercel 以外で Next.js を動かす方法の比べ方は、vinext 1.0の記事で整理しています。

参考資料

この記事を共有XFacebook
鈴木 翔

技術の可能性に魅了され、学生時代からプログラミングとデジタルアートの分野に深い関心を持つ

この記事のテーマを、自社の次の一歩へ

Webサイトで、実現したいことから。

使う人の目的、必要な機能、更新の体制を整理し、制作・改善で最初に取り組むことを考えます。

  • サイトの目的
  • 機能と使いやすさ
  • 公開後の運用
Web制作・改善を相談する

構想段階からご相談いただけます。この記事の情報を相談フォームに引き継ぎます。

最新記事をメールで受け取る・Web制作ガイドを読む
無料ダウンロード

Web制作 費用・発注・集客 完全ガイド【2026年版】

費用相場・制作会社の選び方・集客戦略をPDFにまとめました。

The PDF and newsletter emails are currently in Japanese.

メルマガにも登録されます。いつでも解除可能です。