Next.js May 2026 security release対応入門——認可と依存更新を棚卸しする
Next.js May 2026 security releaseを題材に、App Router、middleware認可、Firestore、GitHub Actionsを安全に棚卸しする実践手順を解説します。

はじめに
Next.js App Routerで業務アプリを作っていて、こんな状態になっていませんか?
| 状況 | リスク |
|---|---|
middleware.ts だけで管理画面を守っている | 迂回経路が出たときにページ側の認可が空になる |
| Server ActionsからFirestoreへ直接書き込む | 権限確認の場所が曖昧になる |
2026年5月7日、Vercelは Next.js May 2026 security release を公開しました。公式発表では、DoS、middleware/proxy bypass、SSRF、cache poisoning、XSSを含む13件のアドバイザリが案内されています。React Server Components由来のCVE-2026-23870も含まれており、Next.js App Routerを使うプロジェクトは無視できません。
本記事では、架空の社内申請SaaS「SecureDesk」を題材に、Next.js + Firestore + GitHub Actions + TypeScriptの構成で、依存更新と認可設計を棚卸しします。
何が発表されたのか
Vercelの発表では、Next.jsの推奨アップグレード先として 15.5.18 または 16.2.6 が示されています。Next.js 13.x/14.xは、15系または16系の修正版へ上げる必要があります。Next.js 15.xは 15.5.18、Next.js 16.xは 16.2.6 が修正済みです。
| 領域 | SecureDeskで見る場所 |
|---|---|
| Server Functions / RSC | Server Actions、Route Handlers |
| middleware / proxy | /admin、/billing、/approvals |
| WebSocket / Cache / Script | 独自Nodeサーバー、共有キャッシュ、CSP nonce |
パッチ適用が最優先ですが、同時に「保護したい画面がmiddlewareだけに依存していないか」「Firestore書き込み前にサーバー側で権限を見ているか」を確認します。
ハンズオン1: 依存を更新する
まず現在のNext.jsバージョンを確認します。
node -p "require('./package.json').dependencies.next"
yarn why next
Next.js 16系なら修正版へ上げます。
yarn add next@16.2.6
yarn build
Next.js 15系なら next@15.5.18 です。react-server-dom-* を直接依存している場合は、その該当バージョンも確認します。
ハンズオン2: middlewareだけの認可をやめる
今回のアドバイザリには、App Routerでmiddlewareやproxyベースの認可に依存するアプリが、特定の経路で保護対象へ到達される可能性があるものが含まれています。すぐにアップグレードできない場合は、ページやルート側でも認可することが示されています。
SecureDeskでは、/approvals 配下を承認者だけが見られる想定にします。middlewareではログイン済みかどうかを早めに見ますが、最終判断はFirestore読み書き直前でも行います。
たとえば requireApprovalRole(userId) というサーバー専用関数を作り、userRoles/{userId} をFirestoreから読みます。role が approver または admin で、かつ disabled でないことを確認してからデータ取得や更新へ進みます。Server Component、Route Handler、Server Actionからこの関数を呼べば、middleware以外にも防御線を置けます。
ハンズオン3: Server Actionの書き込みにも権限を置く
Firestoreへの書き込みも同じです。承認処理では、操作者の権限と対象ドキュメントの状態を確認してから更新します。
"use server";
import { getFirestore } from "firebase-admin/firestore";
import { requireApprovalRole } from "@/lib/auth/require-approval-role";
import { getCurrentUserId } from "@/lib/auth/session";
const db = getFirestore();
type Decision = "approved" | "rejected";
export async function decideApproval(requestId: string, decision: Decision) {
const userId = await getCurrentUserId();
await requireApprovalRole(userId);
if (decision !== "approved" && decision !== "rejected") {
throw new Error("Invalid decision");
}
const requestRef = db.collection("approvalRequests").doc(requestId);
const auditRef = db.collection("auditLogs").doc();
await db.runTransaction(async (tx) => {
const snapshot = await tx.get(requestRef);
if (!snapshot.exists || snapshot.get("status") !== "pending") {
throw new Error("Request is not actionable");
}
tx.update(requestRef, { status: decision, decidedBy: userId });
tx.create(auditRef, {
action: `approval.${decision}`,
requestId,
actorId: userId,
});
});
}
これはNext.jsの脆弱性そのものを直すコードではなく、業務データへの到達点で認可を再確認する防御線です。
ハンズオン4: CIで更新漏れを減らす
依存更新は1回だけでは終わりません。GitHub ActionsでPRごとのbuildを固定します。
name: Next.js Security Check
on:
pull_request:
branches: [main]
workflow_dispatch:
permissions:
contents: read
jobs:
nextjs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: "22"
cache: "yarn"
- run: yarn install --frozen-lockfile
- run: node -p "require('./package.json').dependencies.next"
- run: yarn build
Dependabotなどで依存更新PRを作る場合も、セキュリティPRは小さく保ちます。
導入チェックリスト
- 公式発表とGitHub Security Advisoryで影響範囲を確認する
nextを15.5.18または16.2.6へ上げるyarn buildと主要画面の動作確認を行うmiddleware.ts、proxy.ts、beforeInteractive、WebSocket upgradeの利用を検索する- Firestoreを読む・書くサーバー処理にも認可を置く
- GitHub Actionsで更新確認を継続する
参考リンク
- Next.js May 2026 security release
- Denial of Service with Server Components
- Middleware / Proxy bypass in App Router applications via segment-prefetch routes
- Server-side request forgery in applications using WebSocket upgrades
まとめ
Next.js May 2026 security releaseは、依存更新だけでなく認可設計を見直すきっかけになります。まず修正版へ上げ、yarn build で確認します。そのうえで、Firestoreを読む・書く直前のサーバー処理にも権限確認を置き、GitHub Actionsで同じ棚卸しを継続できる形にしておきましょう。