JWTの「鍵」を使い捨てろ:JWKSによる自動ローテーションの実装と、その裏側にある防衛哲学
こんにちは。現場の最前線でコードとログを追いかけているエンジニア諸君。
今日はJWT(JSON Web Token)の話をしよう。多くのプロジェクトで「HS256(共通鍵)」を使い、環境変数にハードコードされた文字列を使い回している姿を見るが、率直に言おう。それは時限爆弾を抱えて走っているのと同じだ。
万が一、開発者のPCやCI/CDのログから鍵が漏れた瞬間、攻撃者はあなたのアプリケーションの「神(管理者)」になれる。今日は、このリスクを根絶するための「鍵のローテーション」と、それを自動化する「JWKS(JSON Web Key Set)」のエンドポイント実装について、泥臭い実務の視点から解説する。
—
1. なぜ「静的な鍵」は死を招くのか(PoCの視点)
攻撃者がJWTを狙うとき、彼らは「脆弱性」を探すよりも先に「設定ミス」を探す。もしあなたが固定の鍵を使っていれば、攻撃者は以下のステップで侵入を試みる。
1. 鍵の抽出: バックアップファイル、Dockerイメージのレイヤー、あるいはGitHubのコミット履歴から鍵を抜き取る。
2. トークンの偽造: alg: HS256 を指定し、抜き取った鍵で署名した悪意あるJWTを作成する。
3. 権限昇格: sub(ユーザーID)を管理者のものに書き換え、サーバーに送りつける。サーバーは「正しい署名」である以上、これを正当なユーザーと見なす。
これを防ぐ唯一の解は、「鍵は漏れる前提で運用し、頻繁に無効化する(ローテーションする)」ことだ。
—
2. JWKSという「動的な信頼」の仕組み
JWKS(JSON Web Key Set)は、公開鍵をJSON形式で公開するための標準仕様だ。サーバーが今どの鍵を有効にしているかを動的にクライアント(あるいは検証用サービス)に伝えることができる。
- メリット: 鍵を更新しても、クライアント側は
jwks_uriを再取得するだけで追従できる。コードの修正も再デプロイも不要だ。
—
3. 実装サンプル:Python (FastAPI + PyJWT)
バックエンドでJWKSエンドポイントを実装する例だ。ここでは、鍵をメモリに保持し、一定期間でローテーションする設計を想定する。
import jwt
from datetime import datetime, timedelta
from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography.hazmat.primitives import serialization
本来はDBやKMSから取得すべきだが、簡略化のため生成
private_key = rsa.generate_private_key(public_exponent=65537, key_size=2048)
public_key = private_key.public_key()
def get_jwks():
“””JWKS形式で公開鍵を返すエンドポイント用”””
public_numbers = public_key.public_numbers()
return {
“keys”: [{
“kty”: “RSA”,
“kid”: “my-key-id-001”, # 鍵の識別子(ローテーション時はここを変える)
“use”: “sig”,
“alg”: “RS256”,
“n”: format(public_numbers.n, ‘x’), # Base64URLエンコードが必要
“e”: format(public_numbers.e, ‘x’)
}]
}
検証側は、このJWKSを定期的にキャッシュしつつ、
トークンの header[‘kid’] と一致する鍵を使って検証を行う。
—
4. 運用のための「鉄の掟」
コードを書いて終わりではない。運用で事故らないための設定を共有しておく。
Nginxでのキャッシュ制御
jwks_uri は頻繁に叩かれる。サーバー負荷を減らすため、また鍵更新時に即座に反映させるために、適切なキャッシュヘッダーを付与すること。
location /.well-known/jwks.json {
# 鍵の更新を見越して、キャッシュは1時間程度に留める
add_header Cache-Control “public, max-age=3600”;
# 攻撃者からのスパムリクエストに備えてレートリミットをかける
limit_req zone=jwks_limit burst=5 nodelay;
}
AWS KMSとの連携
自前で鍵管理をするのは推奨しない。AWS KMS (Key Management Service) を使い、KMSが自動生成する公開鍵をJWKSとして公開するのが現在のベストプラクティスだ。KMSなら「鍵の自動ローテーション」をAWS側で完結できる。
—
最後に:セキュリティは「継続的な緊張感」
「いつか直す」は「一生直さない」と同義だ。今回紹介したJWKSの実装は、最初は面倒に感じるかもしれない。しかし、一度仕組み化してしまえば、鍵の漏洩という最悪のインシデントに対して「まあ、鍵を差し替えれば済む話だ」と冷静でいられる。
セキュリティとは、システムをガチガチに固めることではない。
「壊れたときに、いかに速く、安全に復旧できるか」という設計思想そのものだ。
明日から、君たちのプロダクトのJWT設定を見直してほしい。鍵をハードコードしている箇所を見つけたら、それが今日のリファクタリングのタスクだ。健闘を祈る。
コメント