Next.jsの脆弱性に5週間気づかなかった|コードを書かない私がAIの点検で見つけ、更新で塞いだ記録
この記事の要点
AIで作ったサイトでも、公開したあとの脆弱性は自分で拾わないと残り続ける。
このサイトのNext.js 16.2.4は、深刻度criticalの脆弱性2件(2026年8月25日と9月22日に公表)の対象だった。
1件目の公表から約5週間後、AIにサイト全体を点検させて初めて気づき、16.3.8へ更新して塞いだ。
npm audit(本番の依存だけ)はcritical 1・high 11からcritical 0・high 3になった。
残りは2日後に片づけて0件にした。脆弱性の知らせを受け取る仕組みは、まだ入れていない。
結論: 公開したサイトの脆弱性は、頼まないかぎり誰も拾わない
先に結論から。
このサイト(AI道具箱)はNext.jsという部品で作っていて、そのバージョン16.2.4が、深刻度critical(最も高い区分)の脆弱性2件の対象だった。
1件目が公表されたのは2026年8月25日。
私が気づいたのは10月1日の夜で、約5週間、対象のバージョンのまま公開していたことになる。
見つけたのは、AIにサイト全体を点検させたときだ。その夜のうちに16.3.8へ更新し、本番で表示を確かめた。
更新のコミットの時刻は20時49分。サイトの設定まわりの直し6件のうちの3件目だった。
私はコードを書かない。このサイトもAIに作らせている。
だからこそ書き残しておきたいのは、AIで作ったサイトでも、公開したあとの脆弱性は自分から点検を頼まないかぎり誰も拾わない、ということだ。
以下、対象だった2件、気づいた経緯、更新の中身、残したものの順に書く。
対象だった2件(2026年10月9日にGitHubで確認)
どちらも、GitHubのセキュリティアドバイザリ(脆弱性の公式の告知)に載っている。2026年10月2日と、この記事を出す10月9日に、対象バージョンと修正版を見直した。
| 1件目 | 2件目 | |
|---|---|---|
| ID | GHSA-2xp9-vwfh-vxw4 | GHSA-vcvr-r3jv-pc5j |
| 公表日 | 2026年8月25日 | 2026年9月22日 |
| 深刻度 | critical(CVSS 9.5) | critical(CVSS 9.5) |
| 中身 | 画像最適化の機能で、AVIF形式の画像を処理するときに、認証なしで外部からコードを実行されうる | next/og の画像生成(ImageResponse)で、外から来た値をSVGに入れて描くと、コードを実行されうる |
| 対象(Next.js 16系) | 16.0.0以上、16.3.3未満 | 16.2.0以上、16.3.6未満 |
| 修正版 | 16.3.3 | 16.3.6 |
16.2.4は、どちらの範囲にも入っている。このバージョンは、2026年6月26日にサイトの土台を作ったときから一度も変えていなかった。
自分のサイトの使い方も照らし合わせた。
画像最適化の機能(next/image)は、記事のページやカードなど4か所で使っていた。
一方、2件目の next/og は、コードのどこにも使っていなかった。
ただ、どちらにしても対象のバージョンで動いていたことは変わらない。実際に外から狙えたのかどうかは、私には判断できない。
だから「使っていなかったから大丈夫」とは書かないでおく。
気づいた経緯——AIに「サイト全体の点検」を頼んだ夜
10月1日、このサイトをAI(Claude Code)に全面的に点検させた。
本文の事実、内部リンク、SEOの技術面、表示の速さ、読み上げへの配慮、コードなど11の観点に分けて、複数のAIに並行して調べさせ、出てきた指摘をまとめると改善の候補は88件。
そのうち72件を採用した。Next.jsの更新は、その中の1件だった。
AIが挙げた脆弱性を、そのまま信じて更新したわけではない。AIはそれらしいIDや対象範囲を書いてしまうことがある。
そこで、更新の前に2つのIDがGitHubのアドバイザリに実在するか、対象の範囲が合っているかを確かめさせ、結果をコミットのメッセージに残した。
上の表の数字は、そのとき残した記録と、翌日と10月9日に開き直した公式の記載が一致したものだ。
更新の中身——Next.jsだけでは済まなかった
更新は、当時同じ16の中で最新だった16.3.8にとどめた。
メジャーバージョンを上げると、書き方の変更に付き合う必要が出やすいからだ。
ところが、Next.jsだけを上げて終わり、とはいかなかった。
| 部品 | 更新前 | 更新後 | 一緒に上げた理由 |
|---|---|---|---|
| next | 16.2.4 | 16.3.8 | 脆弱性2件の修正版を含む |
| eslint-config-next | 16.2.4 | 16.3.8 | Next.jsとそろえる |
| @opennextjs/cloudflare | ^1.19.4 | ^1.20.7 | Next.jsとCloudflareの橋渡し役。最新にそろえた(新しい版はNext.js 16.3.6以上が前提) |
| wrangler | ^4.84.1 | ^4.145.0 | 橋渡し役の新しい版が、wrangler 4.125以上を前提にしている |
| @cloudflare/workers-types | ^4 | ^5 | 古いままだと、npmが依存の食い違いで止まった |
このサイトは、Next.jsで作ったものをCloudflareの上で動かしている。
その橋渡し役の部品も最新にそろえると、今度はそれがCloudflareの道具(wrangler)の新しい版を求め、wranglerがさらに型定義の新しい版を求める、という連鎖で、結局5つを上げることになった。
最後の型定義は、古いままだと npm がインストールの途中で止まったので上げている。
こういう連鎖は、AIが1つずつほどいて報告してくれた。
更新のあとに通した確認は次のとおり。
- 記事の一覧の生成、型チェック、lint、本番用のビルドが通る
- Cloudflare向けのビルドが手元で通る
- 更新を1つのコミットにまとめ、ほかの直しとは分けて公開する(問題が出たら、そのコミットだけ戻せるように)
- 公開後、本番の表示を確かめる
数で見る前後——critical 1・high 11 から critical 0・high 3
サイトを動かすのに使う部品(開発用を除いた分)について、npm audit の結果を更新の前後で比べた。
| critical | high | moderate | |
|---|---|---|---|
| 更新前(10月1日) | 1 | 11 | — |
| 更新後(10月1日) | 0 | 3 | 1 |
| 数え直し(10月2日) | 0 | 3 | 1 |
| 残りを片づけた後(10月3日) | 0 | 0 | 0 |
critical の1件がNext.jsだ。更新でNext.jsは一覧から消え、highも11件から3件に減った。
更新前のmoderateは記録に残していないので「—」にしてある。10月3日の行は、次の節に書く。
残したもの——その夜に直さなかったこと
更新のあとも、highが3件、moderateが1件残った。
どれもNext.jsではなく、ほかの道具の中で間接的に使われている部品(ファイル名の展開、フォームの送信、YAMLの読み込みなど)だ。
どこで使われていて、このサイトの公開中の動作に関わるのかを確かめてから直す、として、その夜は手を付けなかった。
この4件は、2日後の10月3日に片づけた。
サイトの部品の一覧(package.json)は変えず、部品の版を固定しておくファイル(package-lock.json)の5項目だけを新しい版にした。
AIの確かめでは、どれもビルドやデプロイのときに使う部品で、公開中のサイトの動作には載らないものだった。
直したあとの npm audit(開発用を除いた分)は0件になった。
もう1つ、もっと大きな宿題がある。脆弱性の知らせを、自分から探しに行かなくても受け取れる仕組みだ。
今回は点検を頼んだから見つかったが、頼まなければ今も16.2.4のままだったはずだ。
公表から約5週間という数字は、その仕組みが無いことの結果だと受け止めている。この仕組みは、まだ入れていない。
ついでに直したこと
同じ夜、Next.jsの更新の前後で、サイトの設定まわりの直しを公開した。
下の表の①〜④は1件ずつ、残り2件はまとめて公開した。
脆弱性とは別の理由で入れたものだが、守りに関わるものもあるので並べておく。
| 時刻(コミット) | 内容 |
|---|---|
| ① 20時34分 | HTTPSでの接続を続けるようブラウザに伝える設定(HSTS)を足し、使っている部品の名前を応答に出さないようにする |
| ② 20時36分 | 画像最適化の機能を止め、画像をそのまま配信する(画像の配信の流れを単純にするため) |
| ③ 20時49分 | Next.jsほか5つの部品を更新(この記事の本題) |
| ④ 20時54分 | ページのキャッシュの設定を見直す |
| ⑤ 21時01分 | 公開の自動化(CI)に、記事一覧の生成・lint・型チェックの確認を足す |
| ⑥ 21時03分 | 公開しない素材が、手元からの公開に紛れ込まないよう止める確認を足す |
20時36分の画像の変更は、配信の流れを単純にするのが目的で、脆弱性への対策として入れたものではない。ただ結果として、このサイトは画像最適化の機能を使わなくなった。
まとめ: AIで作っても、公開後の点検は自分で頼む
AIに作らせたサイトでも、公開したあとに出た脆弱性は、自分から点検を頼まないかぎり残り続ける。
このサイトのNext.js 16.2.4は、critical の脆弱性2件の対象のまま、1件目の公表から約5週間動いていた。
AIにサイト全体を点検させて気づき、同じ16の中の16.3.8へ更新して塞いだ。npm audit はcritical 1・high 11からcritical 0・high 3になった。
更新そのものより、学んだのは2つだ。AIが挙げた脆弱性は、IDと対象範囲を公式で照合してから動くこと。
そして、知らせを受け取る仕組みが無いかぎり、気づくのは次に点検を頼んだときになること。
後者は、まだ私の宿題として残っている。
このサイトをAIだけで作ってきた過程はバイブコーディングの始め方に、作業を任せているClaude CodeについてはClaude Codeのレビューに書いている。
よくある質問
自分のNext.jsが脆弱性の対象かどうかは、どう確かめればいいですか?
私がやったのは2つです。
1つは、使っているバージョンを確かめること(package.jsonの next の行、または npm ls next)。
もう1つは、GitHubのセキュリティアドバイザリで「対象のバージョン」と「修正されたバージョン」を読むことです。
AIに脆弱性を挙げさせると、IDや対象範囲を取り違えることがあるので、私の場合はアドバイザリが実在するか、範囲が合っているかを公式の記載で照合してから更新しました。
Next.jsのバージョンを上げると、サイトが壊れませんか?
壊れる可能性はあるので、確かめる手順を決めてから上げました。
今回は同じ16の中での更新(16.2.4から16.3.8)にとどめ、メジャーバージョンは変えていません。
型チェック、lint、本番用のビルドを手元で通したうえで、更新を1つのコミットにまとめて単独で公開し、本番の表示を確かめました。
問題が出たらそのコミットだけ戻せる形にしています。
npm auditで出た脆弱性は、全部すぐに直すべきですか?
私は一度に全部は直していません。
今回はcriticalで、しかもサイトの本体であるNext.jsが対象だったものを最優先にしました。
更新後に残ったhigh 3件とmoderate 1件は、どれもNext.jsではなく、ほかの道具の中で間接的に使われている部品だったので、どこで使われているかを確かめてから、2日後に別の直しとして片づけました。
直していないあいだは、直していないと記録に残しています。
AIで作ったサイトは、脆弱性もAIが勝手に直してくれますか?
私のサイトでは、勝手には直りませんでした。
AIは頼んだ作業をしてくれますが、公開したあとに新しい脆弱性が出ても、誰かが点検を頼むまで動きません。
今回もAIにサイト全体の点検を頼んだから見つかったもので、公表から約5週間かかっています。
脆弱性の知らせを自動で受け取る仕組みは、まだ入れていません。
制作記録の更新は Substack でも届けています。
1min Quiz
あなたが作るべきアプリ、1分で診断できます
記事を読んで「自分も何か作ってみたい」と思ったら、まず5つの質問に答えてみてください。いまのあなたに向いたアプリの型と、最初の一歩がわかります。約1分・無料です。