第128章:アクセス制御は「UI」じゃなく「サーバー」でやる🙅🔐
「ログインしてない人にはボタンを隠す」「リンクを出さない」…って、見た目で守った気になりがちだよね🥺 でもそれ、守れてないことが多いの…!💥
結論:アクセス制御は“サーバー側”でやるのが本体です🛡️✨ (UIは“親切”のため、セキュリティは“サーバー”の仕事だよ〜!)
1) なんでUIだけだとダメなの?😵💫
UIで隠しても、叩けちゃう問題🔨
たとえば /dashboard を「メニューから消す」だけにしても…
- URLを手入力されたら?⌨️
- ブックマークから開かれたら?🔖
- 直接
/api/xxxを叩かれたら?🧪 - DevToolsで通信を真似されたら?🕵️♀️
UIは“表示”を変えるだけで、アクセスそのものは止められないんだよね🥲
2) 正しい考え方:守る場所は2つ+α 🧱✨
① 入口で止める:Middleware(門番)🧤
→ まず保護ルートに入る前に止める🚦
② 中でも止める:Server側(ページ/Route Handler/Server Actions)🧊
→ Middlewareをすり抜けても、最終的にサーバーが拒否する🙅
③ さらに:データアクセスで権限チェック(DB等)🗃️
→ 「ログイン済みでも、他人のデータは見せない」みたいなやつ👑
3) 図でイメージしよ〜🧠✨(UIは防壁じゃない)
ポイント:UIは図にすら出てこない(セキュリティの主戦場じゃない)って感じ😉✨
4) 例:/dashboard をサーバーで守る🔐(Next.js App Router)
ここでは「ログイン済みの証拠」として、超ざっくり session Cookie がある前提にするね🍪
(本格的には Auth.js などを使うことが多いよ〜)
✅ 4-1) Middlewareで入口チェック🧤
middleware.ts(プロジェクト直下)を作るよ📄
// middleware.ts
import { NextRequest, NextResponse } from "next/server";
export function middleware(request: NextRequest) {
const session = request.cookies.get("session")?.value;
// ログインしてないなら login へ
if (!session) {
const url = request.nextUrl.clone();
url.pathname = "/login";
// どこから来たかを付けておくと親切✨
url.searchParams.set("from", request.nextUrl.pathname);
return NextResponse.redirect(url);
}
return NextResponse.next();
}
// どのパスに適用するか(保護したい場所だけ!)
export const config = {
matcher: ["/dashboard/:path*"],
};
✨これで、/dashboard に入る前に「門番」が止めてくれる!
✅ 4-2) Server側でも最終チェック(保険)🧊🛡️
Middlewareがあっても、サーバー側のページでも“念のため”チェックするのが強いよ💪 (運用でmiddlewareの設定が変わったり、例外が出たり…そういう時の保険!)
app/dashboard/page.tsx:
// app/dashboard/page.tsx
import { cookies } from "next/headers";
import { redirect } from "next/navigation";
export default function DashboardPage() {
const session = cookies().get("session")?.value;
if (!session) {
redirect("/login");
}
return (
<main>
<h1>Dashboard 🔐</h1>
<p>ログインしてる人だけが見れるよ〜✨</p>
</main>
);
}
✅ これが「アクセス制御はサーバーでやる」の形だよ〜!
5) 「UIだけガード」が危ない例⚠️(やりがち…!)
例えばClient Componentで「ログインしてないなら表示しない」ってやっても…
"use client";
export function SecretPanel() {
const isLoggedIn = false; // 例:状態で判定してるつもり
if (!isLoggedIn) return null; // 表示しない
return <div>秘密情報🤫</div>;
}
これって「見えない」だけで、もし裏でAPI叩いてたら… APIを直接叩かれたら終わりなんだよね😇💥
6) 超重要メモ:localStorage を“ログイン証拠”にしない🙅♀️🧨
localStorageは JavaScriptから読める- XSSが起きたら 盗まれやすい
- 「ログイン判定=localStorage」だと事故りがち🥺
一般的には、ログイン状態は
- **HttpOnly Cookie(JSから読めない)**🍪🔒
- もしくはフレームワーク/ライブラリの仕組み(Auth.js等) に寄せることが多いよ✨
7) まとめ:守る順番はこれ🧱✨
- UI:案内係(便利にする)😊
- Middleware:門番(入口で止める)🧤
- Server:本丸(最終的に拒否できる)🛡️
- DB:所有者チェック(他人のデータは出さない)👑
8) ミニ練習(10分)📝✨
✅ お題:保護ルートを作ろう🔐
-
app/login/page.tsxを作る(中身は「ログインページだよ」って表示でOK)🚪 -
app/dashboard/page.tsxを作る(上の例でOK)📄 -
middleware.tsを置いて、/dashboardを守る🧤 -
ブラウザで
http://localhost:3000/dashboardを開くsessionCookieが無いなら/loginに飛べば成功🎉
(Cookieを手で付けるのは難しいので、今日は「飛ぶのを確認」できたらOKだよ〜😊)
9) この章のゴール🏁✨
✅ 「UIを隠す=守れてる」じゃない!って理解できた ✅ Next.jsでは Middleware + Server側チェックで守るのが基本って分かった ✅ “サーバーが最後に拒否できる状態”が最強って体感した🔐💪