JWT — 取り消せない代わりに、問い合わせが要らない
署名付きトークンが解いた問題と、その代償。「ステートレスだからスケールする」の裏で何を諦めているのか。
先に読んでおくとよい記事
この記事の進み方
What — トークンの中身は誰でも読める
JWT は3つの部分をドットで繋いだ文字列です。暗号化ではなく、署名である点が最重要です。
ヘッダとペイロードは Base64URL エンコードされているだけで、暗号化されていない。誰でもデコードして中身を読める。隠すのではなく、改ざんを検知するための仕組み。
- クレーム
- ペイロードに入る各項目。sub(誰か)、exp(いつまで)、iat(いつ発行)などが標準で定義されている。
- HS256
- 共通鍵方式。発行も検証も同じ秘密鍵を使う。検証する側にも秘密鍵を配る必要がある。
- RS256
- 公開鍵方式。発行は秘密鍵、検証は公開鍵。検証側に秘密鍵を渡さずに済むため、複数サービスで検証したい場合に向く。
- アクセストークン
- API 呼び出しに使う短命なトークン。JWT が使われることが多い。
- リフレッシュトークン
- アクセストークンを再発行するための長命なトークン。サーバーが状態を持って管理するのが一般的。
セッションとの本質的な違い
毎回、保存先に問い合わせる。だから最新の状態が反映される。
1. Cookie から sid を取り出す
2. Redis に sid で問い合わせる ← ネットワーク往復
3. 見つからなければ未認証
4. ユーザー情報を得る(最新)- 保存先への問い合わせ
- 毎回
- 失効
- 1行消せば即座
- 権限変更
- 即座に反映
- サービス間の共有
- 保存先を共有する必要
- –Redis が落ちると全員ログアウトになる。単一障害点になりうる。
- –問い合わせは通常 1ms 未満なので、性能上の問題になることは実はあまりない。
JWT が省いているのは「保存先への問い合わせ」だけ。そして、その問い合わせを省いたことが、そのまま「取り消せない」という性質になる。利点と欠点が同じ1つの事実から出ている。
JWT のペイロードに入れてよくない情報はどれですか?
Why — なぜ「取り消せない」のか
JWT の性質は、たった1つの設計判断から導かれます。
検証時に、発行元へ問い合わせない。
問い合わせないから速い。問い合わせないからサービスをまたいで検証できる。そして、問い合わせないから「このトークンは無効になりました」を知る手段がない。
利点も欠点も、すべてこの1点から出ています。片方だけを取ることはできません。
これは DNS の TTL とまったく同じ構造です。答えを配ったら、期限が来るまで取り消せない。取り消せないから速い。キャッシュを持つシステムが必ず抱える宿命であり、JWT は認証情報をキャッシュしていると見ると理解しやすくなります。
失効させる3つの手段と、その代償
| 手段 | 仕組み | 代償 |
|---|---|---|
| 有効期限を短くする | 5〜15分にして、リフレッシュで更新する | 失効までの猶予は残る。更新の仕組みが必要 |
| ブロックリストを持つ | 無効化したトークン ID を保存し、毎回照合する | 問い合わせが発生し、JWT の利点が消える |
| 鍵をローテーションする | 署名鍵を変えて、全トークンを一斉に無効化する | 全ユーザーが強制ログアウトになる |
実務でよく使われるのは1番目です。アクセストークンを短命にし、失効可能なリフレッシュトークンをサーバーで管理するという組み合わせ。アクセストークン検証は問い合わせ不要のまま、失効の実効性を「数分の猶予つき」で確保できます。
よくある実装ミス
① alg: none を受け入れてしまう
ヘッダの alg をトークン側の申告として読み、none なら署名検証をスキップする実装があります。攻撃者は好きな内容のトークンを作れます。サーバー側で期待するアルゴリズムを固定してください。
② localStorage に保存する
JavaScript から読めるため、XSS が1つあればトークンを丸ごと持ち出されます。HttpOnly Cookie なら、XSS があってもトークン本体は読み出せません。
③ 有効期限を長く取る
「毎回ログインさせたくない」という理由でアクセストークンを数日〜数週間にすると、盗まれたときの被害期間がそのまま長くなり、失効も効きません。長寿命が必要なのはリフレッシュトークンのほうです。
④ ペイロードに機密情報を入れる
メールアドレス、電話番号、内部 ID。Base64 は暗号ではないので、すべて読まれます。
アカウントを停止しても、発行済み JWT で API が通り続けるのはなぜですか?
演習 — まず自分で判断する
解説を読む前に、まず自分で判断してみてください。ここで一度詰まっておくと、 次の節の判断軸が「なるほど」ではなく「そう来たか」に変わります。
この要件を満たす認証設計をどう組みますか?
- 社内向けの経費精算システム。利用者は約 3000 人
- 認証サービス・申請サービス・承認サービス・通知サービスの4つに分かれている
- 退職者のアカウントは、人事システムと連携して即日で無効化する必要がある
- 承認権限は組織変更に伴って変わり、変更は当日から有効にしたい
- ピーク時でも秒間 50 リクエスト程度
- 「マイクロサービスなので JWT でステートレスにすべき」という意見が出ている
- · 「即日無効化」「当日から有効」という要件が、トークンの寿命にどう効くか
- · 秒間 50 リクエストという規模で、状態を持つことは本当に問題か
- · サービスが分かれていることと、認証を自己完結させることは同じ話か
「JWT はステートレスだから採用したい」と言う同僚に、決める前に確認すべきことを説明してください
- 相手はスケールしやすさを理由にしている
- サービスにはアカウント停止の要件がある
- 採用自体を否定するのではなく、条件を詰めたい
読み終わりましたか?
読了にすると、これを前提とする記事がロードマップで開放されます。