JWTはどこに保存すべきか:localStorage・sessionStorage・HTTP-only Cookie・メモリの比較
概要¶
JWT を「どこに保存するか」はセキュリティに直結する設計上の重要な選択。4つの選択肢それぞれのリスクとメリットを整理する。
詳細¶
4つの保存場所の比較¶
| 保存場所 | XSS 耐性 | CSRF 耐性 | 永続性 | 推奨度 |
|---|---|---|---|---|
| localStorage | ✗ 弱い | ✓ 強い | ページ間で維持 | △ |
| sessionStorage | ✗ 弱い | ✓ 強い | タブを閉じると消える | △ |
| HTTP-only Cookie | ✓ 強い | ✗ 弱い(対策必要) | Expires まで維持 | ✓ 推奨 |
| Memory only | ✓ 強い | ✓ 強い | リロードで消える | ✓ 推奨 |
A. localStorage(非推奨)¶
問題: JavaScript から読み取れるため、XSS 攻撃で盗まれる。
- サードパーティスクリプトや注入された JS が localStorage を読める
B. sessionStorage(非推奨)¶
localStorage と同じ XSS リスク。タブを閉じると消えるだけで本質的な違いはない。
C. HTTP-only Cookie(推奨)¶
HttpOnly: JavaScript から読めない(XSS 対策)Secure: HTTPS のみ送信SameSite=Strict: CSRF を大幅に緩和
残るリスク: CSRF(クロスサイトリクエストフォージェリ)
→ 対策: SameSite=Strict + CSRF トークンの二重チェック
D. Memory only(最も安全)¶
// React の例: Zustand や Context に保存
let accessToken = null; // モジュール変数
export function setToken(token) { accessToken = token; }
export function getToken() { return accessToken; }
XSS も CSRF も防げるが、ページリロードで失われる。 → リフレッシュトークンを HTTP-only Cookie に置き、リロード時に再発行する設計が現実的。
推奨アーキテクチャ¶
Access Token → メモリ(有効期限: 15分〜1時間)
Refresh Token → HTTP-only Cookie(有効期限: 7〜30日)
リロード時:
1. Refresh Token Cookie を使って /auth/refresh を呼ぶ
2. 新しい Access Token を取得してメモリに保存
なぜ重要か / いつ使うか¶
- 認証機能を設計・実装するとき
- セキュリティレビューで「トークンはどこに置いてるの?」と聞かれたとき
- 面接で「JWT の安全な扱い方」を聞かれたとき