「一度渡した鍵は返せない」?JWTの盲点と、私たちが作るべき「無効化リスト」の仕組み
こんにちは。現場の最前線でセキュリティと向き合っていると、「JWT(JSON Web Token)はステートレス(状態を持たない)で楽だから」という理由だけで採用し、後から頭を抱えるチームに何度も出会ってきました。
JWTは便利ですが、例えるなら「一度渡したら、有効期限が切れるまで誰が持っていようと家に入れてしまう魔法の鍵」のようなものです。もし、その鍵を落としたり、悪意のある誰かに盗まれたりしたらどうなるでしょう? 有効期限までその泥棒は家に出入りし放題です。
今日は、そんなJWTの弱点を克服するための「失効管理(Revocation)」について、泥臭いけれど確実な防御策を一緒に学んでいきましょう。
—
なぜJWTは「一度渡すと終わり」なのか?
JWTは、サーバー側で「誰に鍵を発行したか」を保存しません。サーバーは、提示されたトークンに書かれた「署名」が正しいかだけを見て、「お、信頼できる鍵だね。入っていいよ」と判断します。
これが「ステートレス」の正体です。しかし、「ログイン後にパスワードを変えた」「スマホを紛失したからアクセスを遮断したい」といった状況になっても、JWTは一度発行されたら、その有効期限が来るまでサーバーは「その鍵がまだ有効か」を自分一人では判断できないのです。
泥棒を締め出すための「ブラックリスト(失効リスト)」
そこで登場するのが、「無効化された鍵のリスト(ブラックリスト)」です。
これは、いわば「泥棒の顔写真リスト」を玄関の警備員(APIサーバー)に渡しておくようなものです。鍵が提示されたら、まずは「この鍵はリストに載っていないか?」を確認する。もし載っていれば、どんなに正しい署名があっても「お前はもう締め出しだ!」と門前払いする。この一手間が、セキュリティを劇的に向上させます。
実装の戦略:Redisで高速な「指名手配書」を作る
このリストをデータベースに保存すると、アクセスするたびに重いクエリが走ってしまいます。そこで、超高速なキーバリュー型ストアであるRedisの出番です。
1. トークンを無効化する処理(ログアウト時など)
ユーザーがログアウトした際や、強制的にセッションを切りたい時に、そのトークンのID(jti: JWT ID)をRedisに保存します。
// ログアウト処理のイメージ
async function logout(token, jti, exp) {
// トークンの有効期限(exp)まで、RedisにIDを保存する
// 期限が来れば自動で消える(EX)ので、ゴミ掃除も不要です
const ttl = exp – Math.floor(Date.now() / 1000);
await redis.set(`blacklist:${jti}`, ‘revoked’, ‘EX’, ttl);
console.log(“この鍵はもう使わせない!ブラックリストに追加しました。”);
}
2. リクエストごとの検証ロジック
次に、APIにアクセスがあった際のチェック処理です。
// APIガードの処理イメージ
async function verifyToken(req) {
const token = req.headers[‘authorization’];
const payload = decodeJWT(token); // トークンを解読
// Redisにそのトークンのjtiが存在するか確認
const isBlacklisted = await redis.get(`blacklist:${payload.jti}`);
if (isBlacklisted) {
throw new Error(“この鍵は既に無効です。締め出します!”);
}
// リストになければ、通常通り処理を続行
return true;
}
—
現場で気をつけるべき「落とし穴」
この仕組みを導入する際、新人の担当者がよく陥る罠がいくつかあります。
- 「すべてのトークンを保存しない」:
ブラックリストには「まだ有効期限内のトークン」だけを入れます。期限が切れたものは自然と無効になるので、Redisに入れる必要はありません。メモリの無駄遣いを防ぐのがプロの腕の見せ所です。
- レイテンシを意識する:
Redisへの通信がボトルネックにならないよう、RedisサーバーはAPIサーバーと同じネットワーク内に配置しましょう。コンマ数ミリ秒の差が、サービスの快適さを左右します。
- 「完璧」を目指さない:
ブラックリストはあくまで「緊急停止スイッチ」です。基本的にはJWTの有効期限(短めにしておくのがコツです)で制御し、ブラックリストは「万が一の備え」として運用しましょう。
最後に:一歩ずつ進んでいきましょう
「JWTは脆弱だから使うな」という極端な意見を耳にすることもあるかもしれません。しかし、正しくブラックリストを実装し、適切な有効期限を設定すれば、JWTは非常に強力な武器になります。
まずは、あなたのプロジェクトのトークンに「jti(JWT ID)」が含まれているかを確認してみてください。そこからが、安全なアプリケーションへの第一歩です。
セキュリティは、一度作って終わりではありません。泥棒のやり口も日々進化しています。でも、今日こうして学んだ「仕組み」があれば、あなたのアプリケーションは確実に強くなっていますよ。
また次の機会に、より深い防御の技術について語り合いましょう。応援しています!

コメント