コードレビューで考えた「認証をミドルウェア層でやるか/やらないか」の設計判断を、勉強のためにまとめたものです。
この記事でわかること
- Next.js ミドルウェアで JWT 検証を「やる/やらない」をどう判断したか
- Edge Runtime の制約のもとでの認証設計の考え方
- 「ミドルウェアと API の二重防御の境界」を意識的に引く意義
概念
Next.js のミドルウェアは Edge Runtime で動作するため、Node.js の crypto モジュールは使えません。ここから「だから JWT の署名検証はミドルウェアではできない」と誤解されやすいが、話はもう少し細かいです。
実は Edge Runtime にも Web Crypto API (crypto.subtle) が用意されていて、jose という JWT/JWS/JWE 用のライブラリを使えば HS256 / RS256 / ES256(いずれも署名のやり方の種類です。ここでは名前だけ押さえれば十分です)などの署名検証は問題なくできます。Next.js 公式の認証ガイドにも、jose を使った middleware での JWT 検証例が掲載されています。
つまり、技術的には Next.js のミドルウェアで JWT の署名検証は可能です。問題は「やるべきかどうか」という設計判断のほうにあります。
修正前のコード
ミドルウェアが access_token クッキーの「存在チェック」のみで保護ページへの通過を許可していた。クッキーに任意の値をセットすれば保護ページの HTML が届いてしまう状態。
// 修正前: 値の中身を検証していない
const token = request.cookies.get('access_token');
if (!token) {
return NextResponse.redirect(loginUrl);
}
// ← token が存在さえすれば通過するなぜ危険か
- 実際の API データは Django で守られているため即時のデータ漏洩ではありません。が、
access_tokenに適当な文字列を入れるだけで保護ページの HTML が返ってしまいます。データ本体は漏れなくても、どんな画面・機能があるか / どの API を叩いているかといった内部構造が見える=本番を攻める前の「偵察材料」を渡すことになります - 「ミドルウェアで守れている」という誤認識が、次の危険な変更を呼びます。たとえば「もう手前で弾いてるから」と API 側の検証を省く、機密データをサーバーコンポーネントで先読みして HTML に埋め込む——といった変更が"安全なつもり"で入りえます。誤認識そのものより、その上に積まれる判断のほうが怖いです
対策の方針
ここで設計判断が必要になる。選択肢は2つあった。
joseを導入してミドルウェアでも JWT を検証する- ミドルウェアは UX 用の簡易フィルターと割り切り、本物のデータ保護は Django API 側に委ねる
最終的に2の方針を選んだ。理由は、
- 本物のデータ保護はどのみち Django API 側でしか担保できません(API を直叩きされたらミドルウェアは通りません)
- ミドルウェアでも JWT 検証してしまうと、署名鍵や検証ロジックを Next.js 側と Django 側の両方に持つ ことになり、二重実装 + 鍵の同期というメンテコストが乗ります
- ミドルウェアの役割は「ログインしてなさそうな人をログイン画面に飛ばす UX の整備」であって、認可の砦ではない、と整理した
つまり「できないからやらない」ではなく、「やろうと思えばできるが、二重防御の境界を整理した上であえて単純なチェックに留める」という設計判断です。意図をコメントとして明記しておく。
// NOTE: このチェックは UX 目的の簡易フィルター。
// 技術的には jose ライブラリで JWT 署名検証も可能だが、
// 本物のデータ保護は Django API 側で担保しているため、
// ミドルウェア側で同じ検証をやると二重実装 + 鍵の同期コストが乗る。
// そのため意図的に存在チェックのみに留めている。
const token = request.cookies.get('access_token');
if (!token) {
return NextResponse.redirect(loginUrl);
}整理:誰がどこを守るか
補足すると、これは2要素認証(パスワード + SMS のような「認証を増やす」話)ではなく、同じ JWT チェックを2層でやるかという二重防御の境界の話です。
学びポイント
- Edge Runtime では Node の
cryptoモジュールは使えないが、Web Crypto API (crypto.subtle) やjoseライブラリ経由で署名検証はできます - 「やったつもりの認証」を作らないために、ミドルウェアと API の二重防御の境界をどう引くかを 設計判断として意識的に決めます
- 「あえて単純なチェックに留める」設計は、コードコメントで意図を明文化するまでが対策です。書き残さないと「直し忘れ」と誤読されます
おわりに
セキュリティ実装で「やれるが、やらない」を選ぶときは、必ず 理由をコードに残します。書き残さないと、未来の自分や他のエンジニアが「直し忘れ」と読んで、安全側のつもりで境界を曖昧にしてしまいます。
ただし、残した理由のほうも腐ります。実際このフィルターのコメントには当初「Edge Runtime では署名検証が困難なため存在チェックのみ」と書いていた。だがこの記事として整理し直すと、正確には「困難」ではなく「jose を使えばできるが、二重実装になるからあえてやらない」という設計判断だった。理由コメントは書いて終わりではなく、記事化のような節目で「その理由はまだ正しいか」を見直すところまでが運用だと整理した。
ミドルウェアと API は別の責務を持つレイヤーです。どこで何を守るか を意識的に決め、片方に依存しすぎない設計にしておくと、後から「片方を強化したら他方の役割が変わった」ときも整理しやすいです。
この記事は 2026 年 4 月時点の自分の理解で書いています。特に Next.js Edge Runtime の制約は変化が速い領域なので、最新の公式ドキュメントも合わせて参照してください。