現場の諸君、お疲れ様。今日もどこかのサーバーで、設定ミスや実装の甘さを突こうと待ち構えている連中がいる。
さて、今回は「JWT(JSON Web Token)」の話だ。最近、「セッション管理をステートレスにしたい」という理由だけで、深く考えずにJWTを導入するプロジェクトが多すぎる。特に危険なのは、「JWTは署名されているから中身は見られても平気だ」という誤解だ。
今日は、なぜJWTのペイロードに機密情報を突っ込むことが「鍵をかけた透明な箱を道端に置く」ような行為なのか、そしてどう防ぐべきかを、泥臭い現場の視点で叩き込む。
—
1. 致命的な勘違い:JWTは「暗号化」ではなく「署名」である
JWTはあくまで「改ざん検知」のための仕組みだ。Base64Urlエンコードされたペイロード部分は、誰でも簡単にデコードして中身を覗ける。
もし君たちが、ユーザーIDだけでなく、メールアドレス、電話番号、あるいは社内の内部IDや権限フラグを平文のままPayloadに詰め込んでいたら、攻撃者は認証を突破せずとも、その情報を盗み見ることができる。
攻撃者の視点(PoCのイメージ)
攻撃者は、キャプチャしたJWTを [jwt.io](https://jwt.io/) のようなサイトに貼り付けるだけで、機密情報を即座に抽出する。これがもし、機密性の高い情報であれば、その時点で「情報漏洩」だ。さらに、その情報を元に「ユーザー情報の書き換え」や「なりすまし」の準備を始める。これが現実だ。
—
2. 原則:機密情報はJWTに入れるな
結論から言おう。「機密情報はJWTのペイロードに含めない」。これが鉄則だ。
JWTには、以下のデータのみを含めるのがベストプラクティスだ。
sub(Subject): 一意なユーザーIDexp(Expiration): 有効期限iat(Issued At): 発行時刻
それ以外の「ユーザーの詳細な属性」が必要なら、JWTにはIDだけを乗せ、バックエンドのキャッシュ(Redisなど)から必要な情報を取得する設計にすべきだ。これが最も安全で、かつ効率的なスケーラビリティを生む。
—
3. どうしても暗号化が必要な場合:JWEという選択肢
「どうしてもクライアントサイドで完結させたい」「サーバーの負荷を極限まで減らしたい」という特殊な要件がある場合、JWTではなくJWE(JSON Web Encryption)を検討する。これはJWTのペイロード自体を暗号化する仕様だ。
ただし、実装コストと運用負荷は跳ね上がる。鍵管理(KMSの選定、ローテーション)ができないチームが手を出すと、逆に脆弱性の温床になるから注意しろ。
Pythonでの実装例(python-joseを使用)
JWEを使って情報を暗号化する際の実装サンプルだ。
from jose import jwe
import json
共有鍵(本来はKMSやVaultで管理すること)
key = b’0123456789abcdef0123456789abcdef’
payload = {
“user_id”: “12345”,
“role”: “admin”, # 機密情報
“exp”: 1735689600
}
JWEとして暗号化(Compact Serialization)
alg: A256GCMKW, enc: A256GCM
jwe_token = jwe.encrypt(json.dumps(payload), key, algorithm=’A256GCMKW’, encryption=’A256GCM’)
print(f”暗号化されたJWT: {jwe_token}”)
—
4. 現場で今すぐやるべき「守りの設定」
コードを書き換える前に、インフラと設計で防げることはやっておく必要がある。
① WAFでJWTの構造を監視する
WAFがJWTの構造(header.payload.signature)を認識しているなら、ペイロードのサイズが異常に大きいリクエストを遮断する設定を入れろ。攻撃者がペイロードに巨大なデータを詰め込んでDoSを狙うのを防ぐためだ。
② ヘッダーに Secure と HttpOnly を徹底する
もしJWTをCookieでやり取りするなら、フロントエンドでのコードはこうなるはずだ。
// Node.js (Express) でのCookie設定例
res.cookie(‘token’, token, {
httpOnly: true, // JSからアクセス禁止(XSS対策)
secure: true, // HTTPS通信のみ許可
sameSite: ‘Strict’, // CSRF対策
maxAge: 3600000 // 1時間で無効化
});
—
5. チーフからの教訓
最後に一つだけ伝えておく。「便利な技術には、必ず隠れたコストがある」。
JWTはステートレスで便利だが、一度発行したトークンを即座に無効化(ブラックリスト化)するのは非常に面倒だ。だからこそ、有効期限は短くし、万が一漏洩しても被害を最小限に抑える設計を心がけろ。
「とりあえず動く」コードを書くのは新人でもできる。君たちに求めているのは、「最悪の事態を想定して、壊れない仕組みを設計すること」だ。
明日のコードレビューで、誰かのJWTの中に怪しいデータを見つけたら、容赦なく指摘してやってくれ。それがチームを守ることになる。では、現場に戻れ。
コメント