Cookie と認証
Cookie の属性、Bearer Token、セッション、OAuth、Passkeys。Web 認証の基礎セットを一通り。
Cookie の基本
サーバが Set-Cookie ヘッダーで送り、ブラウザが保存。同じドメインへのリクエストで自動送信。
Set-Cookie: session=abc123; HttpOnly; Secure; SameSite=Lax; Path=/; Max-Age=3600
Cookie 属性
| 属性 | 意味 |
|---|---|
HttpOnly | JS から読めない(XSS 緩和) |
Secure | HTTPS のみ送信 |
SameSite=Strict | クロスサイト送信なし |
SameSite=Lax | トップレベルナビのみ送信(既定) |
SameSite=None | 常に送信。Secure 必須 |
Path=/ | 送信対象パス |
Domain=.example.com | サブドメイン共有 |
Max-Age=3600 | 秒単位の有効期間 |
Expires=... | 絶対日時 |
Partitioned | 3rd-party 用の分離(CHIPS) |
__Host- プリフィックス | 追加保証(Secure, Path=/, Domain なし) |
__Secure- プリフィックス | Secure 必須 |
SameSite
- Lax(既定)— GET のリンククリックは送る、POST は送らない。CSRF 防止に有効
- Strict — クロスサイトでは絶対送らない。SSO 等を壊すことも
- None — 全ケース送る。Secure 必須。3rd-party Cookie 用
2026 春現在、3rd-party Cookieはブラウザによって制限が進んでいる(Safari ITP、Firefox ETP、Chrome の Privacy Sandbox)。
JS から Cookie を扱う
// HttpOnly でないものだけ
document.cookie = "name=value; path=/; max-age=3600"
// 取得(パース必要)
const cookies = Object.fromEntries(
document.cookie.split("; ").map((c) => c.split("="))
)
// 削除(過去の Expires)
document.cookie = "name=; expires=Thu, 01 Jan 1970 00:00:00 GMT"
新しい Cookie Store API(一部ブラウザ)
await cookieStore.set({ name: "x", value: "1", path: "/", sameSite: "lax" })
const c = await cookieStore.get("x")
await cookieStore.delete("x")
Cookie ベース vs Token ベース
| Cookie | Bearer Token | |
|---|---|---|
| 送信 | 自動 | 明示的に Authorization ヘッダー |
| JS アクセス | HttpOnly で不可 | localStorage 等にあれば XSS で取られる |
| CSRF | SameSite + トークンで対策 | 原則無関係 |
| クロスドメイン | SameSite=None + CORS credentials | CORS の Authorization 許可 |
| モバイルアプリ | 使えるが煩雑 | 素直 |
| 用途 | SSR / 同一オリジン Web | SPA / API / モバイル |
Bearer Token(JWT 等)
await fetch("/api/me", {
headers: { Authorization: `Bearer ${token}` },
})
JWT の構造
header.payload.signature の3部構成(Base64url)。
- 署名で改ざん検知
- ペイロードは暗号化されない(読める)
- 有効期限(
exp)を含めて検証 HS256(共通鍵)orRS256(公開鍵)等
JWT の保存場所
- HttpOnly Cookie(推奨) — XSS 耐性
- localStorage — XSS で抜かれる
- メモリ(変数) — リロードで消える、refresh で再取得
- SecureStore(モバイル) — OS のキーチェーン
Refresh Token パターン
- Access Token(短命、5〜15分)+ Refresh Token(長命、数日〜数週)
- API は Access Token で叩く
- Access が切れたら Refresh Token で新しい Access を取得
- Refresh Token はHttpOnly Cookieに置いて XSS 耐性
セッション(Cookie + サーバ DB)
- ログイン成功時、サーバが
session_idを発行 - DB / KV / Redis に
{ session_id: { userId, expires } } - クライアントには
session_idだけ Cookie で送信 - 毎リクエストでサーバが DB を引いてユーザー特定
メリット: 失効を即時できる(DB から消すだけ)
デメリット: 毎リクエスト DB アクセス
OAuth 2.0 / OIDC(概要)
外部サービス(Google / GitHub / Apple)でログイン。
- ユーザーがログインボタンクリック
- プロバイダの認可画面にリダイレクト(
https://accounts.google.com/...) - ユーザーが許可
- プロバイダがcallback URL に
codeを渡す - サーバが
codeを access_token に交換 - access_token でユーザー情報を取得
- 自社のセッション / JWT を発行
PKCE(モバイル / SPA で必須)
Proof Key for Code Exchange。code_verifier と code_challenge で
途中経路で code が盗まれても安全に。
主要プロバイダ
- Google / GitHub / Apple / Facebook / Microsoft / X
- Auth0 / Clerk / Supabase / WorkOS が SaaS としてラップ
WebAuthn / Passkeys
パスワードを使わない認証。FIDO2 / WebAuthn標準。Apple / Google / Microsoft が推進。
// 登録
const credential = await navigator.credentials.create({
publicKey: {
challenge: ...,
rp: { name: "My App", id: "example.com" },
user: { id: ..., name: "user@example.com", displayName: "User" },
pubKeyCredParams: [{ type: "public-key", alg: -7 }],
authenticatorSelection: { userVerification: "required" },
},
})
// 認証
const assertion = await navigator.credentials.get({
publicKey: { challenge: ..., allowCredentials: [...] },
})
サーバ側ライブラリ: SimpleWebAuthn。プロバイダ統合: Passage / Hanko。
CSRF 対策
- SameSite=Lax(既定)で大半防げる
- CSRF トークン — フォームに hidden で埋め込み、サーバで検証
- Origin / Referer ヘッダー検証
- Bearer Token 認証なら CSRF は原則無関係(明示的に Authorization が要るため)
XSS 対策(Cookie 保護のため)
- HttpOnly Cookie で JS から守る
- Content-Security-Policy(CSP)でスクリプト実行を制限
- ユーザー入力をエスケープ(React 等は自動)
innerHTML禁止、textContent- サードパーティスクリプトを最小限に
3rd-party Cookie の終焉
- Safari は ITP で事実上ブロック済み
- Firefox の ETP も同様
- Chrome は Privacy Sandbox 移行中(完全廃止は度々延期)
- SSO や iframe ベース連携はStorage Access API や CHIPS(Partitioned)で対応
CHIPS(Cookies Having Independent Partitioned State)
Set-Cookie: id=123; SameSite=None; Secure; Partitioned
埋め込み iframe ごとにパーティション化された Cookie を持てる。
認証ライブラリ・SaaS
| サービス | 特徴 |
|---|---|
| Clerk | UI 込みフルマネージド。React / Next.js で楽 |
| Auth0 | 古参の定番。エンタープライズ機能豊富 |
| Supabase Auth | Supabase の一部。OAuth / Magic Link / Passkey |
| WorkOS | B2B SSO(SAML / SCIM)に強い |
| Better Auth | 新興。Hono / Next.js / TanStack 連携 |
| NextAuth (Auth.js) | OSS。Next.js 中心だが他にも対応 |
| Lucia | 軽量・自前実装寄り |
| Cloudflare Access | CF Workers/Pages 環境向け |
✓ HttpOnly Cookie + SameSite=Lax / Bearer + 適切な保存場所
✓ HTTPS / Secure 属性
✓ パスワードは bcrypt / scrypt / argon2 でハッシュ
✓ レート制限(ブルートフォース対策)
✓ 2FA / Passkey / SSO の対応
✓ ログアウトでセッション無効化
✓ XSS / CSRF 対策