パッチ2026年8月
Patched 2026/08
2026年8月に実施した、本アプリの追加機能、問題点の修正をまとめます。
- 機能追加
なし - 修正
- Auth.js(
next-auth)を脆弱性修正版へ更新 - Next.jsをServer Actions関連の脆弱性修正版へ更新
- 画像アップロードと
/media配信を、未認証利用、偽装ファイル、巨大ファイル、active content、キャッシュ誤設定に耐える構成へ変更
- Auth.js(
修正
1. Auth.js(next-auth)を脆弱性修正版へ更新
更新前は以下のバージョンを使用していました。
"next-auth": "^5.0.0-beta.31"
5.0.0-beta.31以前では、Auth.js内部の設定エラーなどが発生した際、本来未認証として扱うべき状態がtruthyなエラーオブジェクトになる可能性があったようです。
ミドルウェアやAPIルート等で auth() を用いて認証チェックを行う際、サーバー側の設定ミス(環境変数 AUTH_SECRET の未設定やプロバイダーの記述ミスなど)が発生すると、通常ならアクセスを遮断すべきところ、Auth.jsがエラーオブジェクト(Truthyな値)を返していました。
そのため、次のようなauthの存在だけを確認する認証処理では、異常時に未認証ユーザーを認証済みと誤認するリスクがありました。
if (!req.auth) {
// loginへ
}
という判定をしていたとします。
正常な未ログインなら、
req.auth === null
なので、
!!req.auth === false
になります。
ところが beta.31 以前では、Auth.jsの設定エラーなどが起こると、本来 null にすべきところに概念的には次のようなオブジェクトが入るケースがありました。
{
message: "There was a problem with the server configuration..."
}
これはJavaScriptではオブジェクトなので、
!!req.authがtrueになってしまう可能性があったようです。
参考:
今回行った主な対処が以下です。
next-authを5.0.0-beta.32へ更新- センシティブな処理は、セッション情報だけでなくDBから現在のユーザー状態とroleを再取得して確認
- 認証処理やDB確認で例外が発生した場合は拒否するfail-closed方式を採用
ところで、元のコードにこのような認可がありました。
const session = await auth();
const role = session?.user?.role;
const canPost = role === "admin" || role === "editor";
if (!session?.user || !canPost) {
return {
success: false,
errors: {
form: ["権限がありません(ログイン or 権限不足)"],
},
};
}
認証していない人がここを叩いてもconst session = await auth();の時点で本来エラーが出ますが、auth.jsのどこかに問題があった場合、ここが通りsessionに何らかの値が入ります。
ただし、それでも
const role = session?.user?.role;
const canPost = role === "admin" || role === "editor";
をしてif (!session?.user || !canPost) {でユーザー情報やロールが正しくなければエラーが投げられるようになっていたので致命的な問題とはなっていませんでした。codexさまさまです。
2. Next.jsをServer Actions関連の脆弱性修正版へ更新
更新前はNext.js 15.5.18を使用していました。
最近忙しく、更新できていませんでした。
このアプリ構成ではApp Router、Server Actions、OpenNext、Cloudflare Workersを利用しており、巨大なServer Action payloadによるメモリ消費や、Server Actionを外部から特定・直接呼び出される可能性がある脆弱性があったようで、特に画像アップロード処理と組み合わさると大容量データによるWorkerメモリ、R2容量、転送量の悪用につながるリスクがありました。
参考:
3. 画像アップロードと/media配信を、未認証利用、偽装ファイル、巨大ファイル、active content、キャッシュ誤設定に耐える構成へ変更
そもそも、大きな問題として画像保存に関する認証・認可のコードがなぜかコメントアウトされていました。
// const session = await auth();
// const role = (session?.user as any)?.role;
// if (!session?.user || !canCreate(role)) {
// return { ok: false, error: "権限がありません" };
// }
本来これを直すだけで良いのですが、この際なのでいくつか気になっていたところを直しました。
おそらくこうしていたのは、server actionを正しいページからしか届かないから大丈夫だとたかを括っていたのだと思います。
しかし、next.jsの脆弱性報告を確認しているとserver actionを不正なルートからアクセスできる報告が度々見られるため、当たり前ですがserver actionでもユーザー認証・認可は強くしておいた方が良いです。
そこで特定のserver actionに関係する認証・認可を以下のような構造にしてまとめました。
export async function getActiveContentEditor() {
try {
const session = await auth();
// sessionチェック
const db = getDb();
const user = await db.query.users.findFirst(...);
// DB上のuserチェック
return { id: user.id, role: user.role };
} catch {
console.error(...);
return null;
}
}
1つ目の脆弱性に対しての対策の、fail-closed構成も心がけています。
また、もう一点LLMに促されて画像形式判定をもう少し厳しくしました。
具体的なコードは載せませんが、"file-type": "21.3.4"を使って
クライアントから送られてきたfile.typeの値は信用せずに実画像データを解析してファイル形式を検証するようにしました。
おわりに
今回の総括として、大きな問題が2つありました。
1つ目は、定期的なアプリのアップデートができていなかったことです。
AIの発展により脆弱性の発見スピードがかなり上がっています。一度開発したアプリを保守するにはこのスピードについていかなければなりません。しかし、これに管理者がだけでついていくのは少し難しいように思います。
そこで今回、ChatGPTのgithubプラグインとスケジュールの機能を合わせて定期的にこのブログのコードの検証をさせて問題がある場合は管理者に通知がいくようにしました。
これで瞬時にとは言えませんが、今よりはるかに早く脆弱性への対処の実装ができるようになると思われます。
もうひとつは、フルスタックアプリでもある程度クライアントとサーバーを意識して実装するべきだということです。
例え server actionでも正しく設計していないと不正アクセスされます。
サーバーの実装はクライアントを完全に信用した設計にはしないほうが良いです。フルスタックフレームワーク(またはライブラリ)は記述が便利な反面このクライアントとサーバーサイドの境界が時折合間になることがあります。
少し過激なことを言うと、もしかするとAIがほぼ全てのコードを書くこの時代において「記述が便利」と言う側面はそこまで重視されなくなってくるような気がします。
もちとん、「記述が便利」と言うことは「コードが読みやすい」ことも意味し、生成されたコードを読む必要がある現在は良い点かもしれません。
しかし、それを言うのなら hono + react のようにサーバーとクライアントを明確に分けて実装したほうが良いのではとも思ってしまいます。
昨今一部の界隈で、「next.js 不要論」が囁かれています。
このアプリはcloudfalreの上で動いているのですが、v8 Isolates 上でNext.js を動かすには基本的にopen nextを挟む必要があり、それでもNext.jsの機能の一部制限やバグが起こりがちです。
nextjsは私が初めて学習したフルスタックフレームワークであり愛着があるのです(あとロゴがすごくカッコいい!)が「cloudfalre と nextjsのどちらを取る」といわれると迷うこともないでしょう。
個人開発においてcloudfalreは必要不可欠です。
だからと言ってすぐに変えるつもりはありませんが、長い目で今後の動向を見て判断していこうと思います。
最後の方は大きく話がずれましたがこれで終わります。
Author
主にシステム面で学習したことをまとめています。 フロント、サーバー、インフラ、AIなど細かい分野に絞らずに広く発信していきます。