仕組みから学ぶ Web
応用読了目安 14#JWT#トークン#署名#認証

JWT — 取り消せない代わりに、問い合わせが要らない

署名付きトークンが解いた問題と、その代償。「ステートレスだからスケールする」の裏で何を諦めているのか。

この記事の進み方

What — トークンの中身は誰でも読める

JWT は3つの部分をドットで繋いだ文字列です。暗号化ではなく、署名である点が最重要です。

FigureJWT の構造
ヘッダ+ eyJhbGciOiJIUzI1NiJ9どのアルゴリズムで署名したか{"alg":"HS256","typ":"JWT"}
ペイロード+ eyJzdWIiOiI0MiIsImV4cCI6...主張したい内容(クレーム){"sub":"42","exp":1735689600}
署名+ dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk改ざんされていないことの証明HMACSHA256(header + payload, secret)
実際に送られる文字列
eyJhbGciOiJIUzI1NiJ9データ本体
1/3
ヘッダ署名アルゴリズムを宣言する。ここを検証側が鵜呑みにすると、alg:none 攻撃の余地が生まれる。サーバーは期待するアルゴリズムを固定すべき。

ヘッダとペイロードは Base64URL エンコードされているだけで、暗号化されていない。誰でもデコードして中身を読める。隠すのではなく、改ざんを検知するための仕組み。

クレーム
ペイロードに入る各項目。sub(誰か)、exp(いつまで)、iat(いつ発行)などが標準で定義されている。
HS256
共通鍵方式。発行も検証も同じ秘密鍵を使う。検証する側にも秘密鍵を配る必要がある。
RS256
公開鍵方式。発行は秘密鍵、検証は公開鍵。検証側に秘密鍵を渡さずに済むため、複数サービスで検証したい場合に向く。
アクセストークン
API 呼び出しに使う短命なトークン。JWT が使われることが多い。
リフレッシュトークン
アクセストークンを再発行するための長命なトークン。サーバーが状態を持って管理するのが一般的。

セッションとの本質的な違い

Compareリクエストごとに何が起きるか

毎回、保存先に問い合わせる。だから最新の状態が反映される。

1. Cookie から sid を取り出す
2. Redis に sid で問い合わせる  ← ネットワーク往復
3. 見つからなければ未認証
4. ユーザー情報を得る(最新)
保存先への問い合わせ
毎回
失効
1行消せば即座
権限変更
即座に反映
サービス間の共有
保存先を共有する必要
  • Redis が落ちると全員ログアウトになる。単一障害点になりうる。
  • 問い合わせは通常 1ms 未満なので、性能上の問題になることは実はあまりない。

JWT が省いているのは「保存先への問い合わせ」だけ。そして、その問い合わせを省いたことが、そのまま「取り消せない」という性質になる。利点と欠点が同じ1つの事実から出ている。

確認 — ここまで読めたか

JWT のペイロードに入れてよくない情報はどれですか?

まず選ぶ(解答例は a〜d の記号で説明します)

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 が通り続けるのはなぜですか?

まず選ぶ(解答例は a〜d の記号で説明します)

演習 — まず自分で判断する

解説を読む前に、まず自分で判断してみてください。ここで一度詰まっておくと、 次の節の判断軸が「なるほど」ではなく「そう来たか」に変わります。

演習 — 設計判断を問う

この要件を満たす認証設計をどう組みますか?

与えられた条件
  • 社内向けの経費精算システム。利用者は約 3000 人
  • 認証サービス・申請サービス・承認サービス・通知サービスの4つに分かれている
  • 退職者のアカウントは、人事システムと連携して即日で無効化する必要がある
  • 承認権限は組織変更に伴って変わり、変更は当日から有効にしたい
  • ピーク時でも秒間 50 リクエスト程度
  • 「マイクロサービスなので JWT でステートレスにすべき」という意見が出ている
この軸で考える
  • · 「即日無効化」「当日から有効」という要件が、トークンの寿命にどう効くか
  • · 秒間 50 リクエストという規模で、状態を持つことは本当に問題か
  • · サービスが分かれていることと、認証を自己完結させることは同じ話か
まず選ぶ(解答例は a〜d の記号で説明します)

演習 — 説明できるか

「JWT はステートレスだから採用したい」と言う同僚に、決める前に確認すべきことを説明してください

与えられた条件
  • 相手はスケールしやすさを理由にしている
  • サービスにはアカウント停止の要件がある
  • 採用自体を否定するのではなく、条件を詰めたい

読み終わりましたか?

読了にすると、これを前提とする記事がロードマップで開放されます。

この記事を前提にしている記事