JWT入門から実務まで:基本構造・クレーム・署名・セキュリティ対策

はじめに — JWT とは何か

JWT(JSON Web Token)は、JSON 形式のクレーム(情報)を安全に表現・送受信するための標準フォーマットです。主に認証・認可や情報の伝達に使われ、「署名」により改ざん検知を可能にしたコンパクトなトークンとして広く利用されています。正式仕様は IETF の RFC 7519 に定められ、JOSE(JWS/JWE/JWK など)の枠組みの一部として設計されています。

基本構造

JWT は「ヘッダー . ペイロード . 署名」の3つの部分からなります。各部分は Base64url エンコードされ、ピリオド(.)で連結されます。

  • ヘッダー(Header):トークンのタイプ(通常は "JWT")と署名アルゴリズム(例:"HS256", "RS256")を含む JSON。
  • ペイロード(Payload):クレーム(claims)と呼ばれる利用者やトークンに関する情報を表す JSON。(iss, sub, aud, exp など)
  • 署名(Signature):ヘッダーとペイロードを結合して署名アルゴリズムで生成した署名。改ざん検知と発行者の検証に使用。

簡単な例

概念をつかみやすくするための単純化した例(説明用であり実際の値は短縮しています):

ヘッダー(JSON):
{"alg":"HS256","typ":"JWT"}

ペイロード(JSON):
{"sub":"1234567890","name":"Taro Yamada","iat":1516239022,"exp":1516242622}

トークン例:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IlRhcm8gWWFtYWRhIiwiaWF0IjoxNTE2MjM5MDIyLCJleHAiOjE1MTYyNDI2MjJ9.
<署名>

よく使われるクレーム(登録済みクレーム)

  • iss:発行者(issuer)
  • sub:主体(subject)— 一般的にユーザーIDなど
  • aud:受信者(audience)— このトークンが想定される受け手
  • exp:有効期限(expiration time)— UNIX 時刻で表現
  • nbf:利用開始時刻(not before)
  • iat:発行時刻(issued at)
  • jti:一意なトークン識別子(JWT ID)

これらの登録済みクレームに加えて、独自のカスタムクレームを追加することも可能です。

署名アルゴリズムと暗号化

JWT 自体は署名(JWS: JSON Web Signature)により整合性と発行者の認証を提供します。主なアルゴリズムは:

  • HS256(HMAC + SHA-256):対称鍵方式。発行側と検証側が同じ秘密鍵を共有する場合に使います。
  • RS256(RSASSA-PKCS1-v1_5 + SHA-256):公開鍵暗号方式。発行側は秘密鍵で署名し、検証側は公開鍵で検証します。公開APIなどに向く。
  • ES256(ECDSA + SHA-256):楕円曲線署名。短い鍵長で高い安全性。

トークンの暗号化(機密性確保)が必要な場合は、JWE(JSON Web Encryption)を使って暗号化された JWT をやり取りします。多くの実装では、署名(JWS)だけで十分なケースが多いですが、ペイロードに機密情報を入れるべきではありません。

利用シナリオ

  • シングルページアプリ(SPA)やモバイルアプリでの認証・認可(Bearer トークンとして Authorization ヘッダに設定)
  • マイクロサービス間の認証情報伝搬(トークンにユーザーやスコープ情報を含めることで、状態を持たない認可判断が可能)
  • OAuth 2.0 のアクセストークンや OpenID Connect の ID トークンとしての利用(ただしアクセストークンが JWT とは限らない)

セキュリティ上の注意点(重要)

JWT は便利ですが、誤った実装や運用で重大な脆弱性を招くことがあります。代表的な注意点を挙げます。

  • 署名を検証すること:受信したトークンは必ず署名検証を行い、発行者(iss)や受信者(aud)をチェックすること。
  • アルゴリズムの固定化:トークンのヘッダーで指定された alg を鵜呑みにせず、サーバ側で許可するアルゴリズムを明示的に制限する("alg":"none" 攻撃やアルゴリズム切替攻撃を防ぐ)。
  • 機密情報をペイロードに入れない:ペイロードは Base64url エンコードされるだけで暗号化されない。誰でもデコード可能なので、パスワードや機密データは入れない。
  • HTTPS を必須にする:JWT は Bearer トークンとして扱われるため、盗聴で容易に悪用される。常に TLS(HTTPS)で送信する。
  • 有効期限と短い寿命:アクセストークンの寿命を短く設定し、長期的なセッション管理にはリフレッシュトークンと組み合わせる。リフレッシュトークンはより厳重に保護する。
  • トークンの取り消し(リボーク):JWT はステートレスであるため、発行後の取り消しが困難。必要ならブラックリスト(DB)やトークンID(jti)を使った照会、または短期間有効+リフレッシュで対処。
  • 保存場所のリスク:ブラウザでは localStorage に保存すると XSS に弱い。セキュアな HttpOnly Cookie(SameSite)を使う、もしくは適切な CSRF 対策を行う。
  • キー管理:秘密鍵や共有鍵のローテーション、保護が重要。公開鍵を配る場合は JWKs エンドポイント(公開鍵の JSON 形式)を使って動的に取得・検証することが推奨される。

実運用でのベストプラクティス

  • 短いアクセストークンの有効期限(例:数分〜数時間)と、より慎重に管理されたリフレッシュトークンを組み合わせる。
  • 署名検証時に iss, aud, exp, nbf, iat を必ずチェックし、時計のずれを考慮したスキュー(例:数十秒〜数分)を許容する。
  • 公開鍵方式(RS256 など)を可能な限り採用し、API 側は署名検証のみで済ませられるようにする。キーは kid を使って識別・ローテーションする。
  • トークンのリボークが必要な場合は、jti を DB に保存してブラックリストチェック、またはトークンインテロスペクション(OAuth 2.0 の仕様)を導入する。
  • CORS/CSRF/XSS 対策を怠らない。SPA でのトークン保存は特に慎重に。
  • ペイロードには最小限の情報のみを入れ、センシティブなデータはサーバ側で管理する。

よくある誤解・落とし穴

  • 「JWT は盗まれても安全」ではない:JWT は有効期限中は Bearer トークンとして扱われ、持っているだけでアクセス可能。
  • 「署名された=機密性がある」ではない:署名は改ざん検知のみで、暗号化(JWE)されていなければ内容は平文同様に見える。
  • 「アクセストークンは常に JWT」ではない:OAuth2 のアクセストークンは実装により任意で、必ずしも JWT とは限らない。サービスによっては不透明(opaque)なトークンを返す。

まとめ

JWT は、スケーラブルでステートレスなトークンベースの認証・認可を実現する強力なツールです。正しく実装すればマイクロサービスやモバイル/SPA における認証基盤をシンプルにできます。一方で、署名検証、アルゴリズムの扱い、保存場所、鍵管理、トークンリボークの運用など、注意すべき点も多く、セキュリティ設計を怠ると大きなリスクを招きます。実装時は公式仕様(RFC)や OWASP のガイドライン、最新のベストプラクティスを参照し、安全な運用を心がけてください。

参考文献