Next.jsのlayout.tsxで認証チェックすると情報漏洩するかも
Next.jsの認証チェックどこでするか問題
基本的には middleware.ts
で行うと思うのですが、肥大化を避けたり、ちょっとした共通処理は layout.tsx
に書くこともあるでしょう。今回は layout.tsx
で認証チェックをした場合に、実装によっては意図せず認証ユーザにしか表示したくない情報が漏洩してしまうかもしれないケースを紹介します。
問題のあるコード
import { redirect } from "next/navigation";
export const dynamic = 'force-dynamic';
function currentUser() {
// ここでセッションデータから認証ユーザ情報を取得する関数
// デモ用にログインしていないユーザを再現したいのでfalseを返す
return false;
}
export default function Layout({ children }) {
// ログインしていないユーザは、ログインページへリダイレクトさせる
if (!currentUser()) {
redirect('/login');
}
return (
<div>{children}</div>
);
}
async function fetchData() {
// データベースから情報を取得する
const data = await database.query();
// data には「秘匿情報だよ」という文字列が入っているとする
return data;
}
export default async function Page() {
// 本来ログインしているユーザにしか表示したくないデータ
const data = await fetchData();
return (
<div>{data}</div>
)
}
さてこのページにブラウザでアクセスをすると /login
ページにリダイレクトされます。これでちゃんと認証チェックが動いているように感じますが、実は違います。今度はブラウザではなく、curlで試してみましょう。
# レスポンスヘッダも見たいので --include オプションを付ける
curl --include http://localhost:3000/protected
すると以下のような結果が得られます。
HTTP/1.1 307 Temporary Redirect
Location: /login
// 略
<script>self.__next_f.push(
[1,"4:[\"$\",\"div\",null,{\"children\":\"秘匿情報だよ\"}]\n
8:[[\"$\",\"meta\",\"0\",{\"name\":\"viewport\",\"content\":\"width=device-width, initial-scale=1\"}],[\"$\",\"meta\",\"1\",{\"charSet\":\"utf-8\"}]]\n3:null\n"])</script><script>self.__next_f.push([1,""])</script></body></html>⏎
Location
ヘッダを使って /login
へリダイレクト指示されていますが、レスポンスボディもあります。そしてその中をよく見てみると…「秘匿情報だよ」という文字列がありますね。本来であればログインしているユーザにしか表示したくないデータです。しかし、このようにcurlでリダイレクト前のボディに含まれてしまっています。危ないです。
これはNext.jsのレイアウトとページの処理順が関係しています。実はページ→レイアウトの順に処理されます。非同期処理の場合など細かくは異なるので詳細はこちらの記事を参照してください。
改善するには?
素直に middleware.ts
で認証チェックするのが良いと思いますが、それ以外にも回避策はあります。layout.tsx
ではなく情報を実際に出力するコンポーネントで認証チェックをすることです。
export default async function Page() {
// 本来ログインしているユーザにしか表示したくないデータ
if (!currentUser()) {
redirect('/login');
}
const data = await fetchData();
return (
<div>{data}</div>
)
}
これであれば秘匿情報が事前に配信されることはありません。
まぁ、実際のアプリケーションではここまで雑な条件が揃うことはないので、問題ないかもしれませんが知らずに開発をしていると恐ろしいことになるかもしれないので気をつけましょう(自戒)。
Discussion
仕様変更されるような情報を見かけたのでこちらにも情報を共有します