flawopen.com/リファレンス/JWT と HttpOnly クッキー
localStorage の JWT と HttpOnly クッキー:どちらが安全か
多くのWebアプリでは、サーバー管理のセッションを HttpOnly Cookie で受け渡す方式が堅実な出発点です。JavaScriptからCookieを読めないため、トークンの直接窃取を抑えられますが、XSS自体は無効化されません。同一オリジンで動く注入コードは、認証済みリクエストを送り、ページから参照できるデータを読む可能性があります。
セキュリティ上のトレードオフ
| 保存 / 送信 | XSSのリスク | CSRFへの影響 | 失効 |
|---|---|---|---|
localStorage bearer token | 同一オリジンのJavaScriptから読め、XSSで窃取またはユーザーとして操作される可能性があります。 | クロスサイトリクエストに自動添付されませんが、XSSやクライアントサイドCSRFは残ります。 | 失効を確認しなければ、自立型JWTは期限まで有効な場合があります。 |
HttpOnly cookie | JavaScriptから値を読めませんが、XSSで認証済みリクエストを送ることは可能です。 | Cookie規則に従って自動送信されます。SameSiteはCSRF対策の一層にすぎません。 | 不透明なセッションはサーバーで無効化できます。JWT Cookieには別途失効方針が必要です。 |
推奨アーキテクチャ
- ✓セッション識別子は
HttpOnly; Secure; SameSite=Laxクッキーに保存する。 - ✓永続的な認証トークンを
localStorageやsessionStorageに保存しない。
トークン形式・保存場所・セッション状態を分けて考える
JWTはトークン形式です。localStorageとCookieはブラウザーでの保存・送信方法で、JWTはどちらにも保存できます。Cookie内の不透明なセッション識別子は通常サーバー側の状態を参照します。「JWTかCookieか」は同列の選択肢の比較ではありません。
方式ごとに変わる点
ブラウザーは localStorage の値をクロスサイトリクエストに自動添付しませんが、同一オリジンのXSSはBearerトークンを読んだり、アプリを通じて操作したりできます。Cookieは規則に従い自動送信されます。HttpOnlyはJavaScriptから値を隠しますが、注入コードがログイン済みページを利用するのは防げません。SameSite=LaxはCSRF対策の一層であり、完全な保証ではありません。安全なメソッドによるトップレベル遷移やsame-siteリクエストも考慮し、GETで状態を変更せず、検証済みトークンやOrigin検証など適切な対策を使います。
失効は保存先ではなく、サーバー状態とトークン検証に依存します。サーバー側セッションは記録を削除して無効化できます。失効状態を確認しない場合、自立型JWTは期限まで受理される可能性があります。短い有効期間はその時間を縮めますが、なくしません。
実務上の出発点
- ✓自社Webアプリでは、構成に合う場合、不透明なサーバー側セッションまたはBackend-for-Frontend(BFF)を優先します。
- ✓セッションCookieに
Secure、HttpOnly、意図して選んだSameSite、Path=/を設定し、可能ならDomainを省き、__Host-も検討します。 - ✓脅威モデルに応じて状態変更リクエストにCSRFトークンやOrigin検証を使います。
GETで状態を変更してはいけません。 - ✓ブラウザー内JWTが必要なら有効期間を短くし、ログアウトと失効方針を定め、XSSを防ぎます。Cookie保存は認可確認の代わりになりません。