本記事はGeminiの出力をプロンプト工学で整理した業務ドラフト(未検証)です。
RFC 7519 JSON Web Token (JWT)の構造と検証
背景
WebアプリケーションやAPIが分散化・マイクロサービス化するにつれて、ステートレスな認証および認可のメカニズムが求められるようになりました。従来のセッションベースの認証システムは、サーバ側でセッション状態を管理する必要があり、スケーラビリティやCORS (Cross-Origin Resource Sharing)、CSRF (Cross-Site Request Forgery) 対策に課題を抱えていました。
このような背景から、クライアント側で認証情報を保持し、サーバ側では状態を持たずに検証できるトークンベースの認証が注目されるようになりました。特に、OAuth 2.0などの認可フレームワークにおいて、アクセストークンとして利用されることが増えています。RFC 7519 JSON Web Token (JWT) [1] は、この要件を満たすためのコンパクトでURLセーフな方法を提供します。
設計目標
RFC 7519は、以下のような目標を掲げて設計されました。
コンパクトさ: HTTPヘッダやURLパラメータとして容易に送信できるよう、サイズを小さく保つ。
URLセーフ: URLやHTTPヘッダで安全に転送できるよう、Base64urlエンコードを使用する。
自己完結性: トークン自体が必要なすべての情報(ユーザ情報、有効期限、発行者など)を含むため、データベースへの参照が不要。これにより、サーバの負荷を軽減し、スケーラビリティを向上させる。
検証可能性: トークンの発行者と整合性を暗号学的に検証できること。これにより、改ざんや偽造を検出できる。
JSONベース: 人間が読みやすく、多くのプログラミング言語で容易にパースおよび生成できるJSON形式を採用。
詳細
RFC 7519で定義されるJWTは、実際にはJSON Web Signature (JWS) [2] の一種であり、署名によってその完全性が保護されます。オプションでJSON Web Encryption (JWE) [3] を使用して暗号化することも可能ですが、RFC 7519はJWSとしての構造を主に定義しています。
JWTの基本構造
JWTは、通常3つの要素を . (ドット) で連結したコンパクトな文字列として表現されます。
Base64URL(JWS Header).Base64URL(JWS Payload).Base64URL(JWS Signature)
flowchart LR
subgraph SUBGRAPH_SERIAL["JWTコンパクト表現"]
HEADER_B64["Base64URL(#quot;JWS Header#quot;)"] --> DOT1{.}
DOT1 --> PAYLOAD_B64["Base64URL(#quot;JWS Payload#quot;)"]
PAYLOAD_B64 --> DOT2{.}
DOT2 --> SIGNATURE_B64["Base64URL(#quot;JWS Signature#quot;)"]
end
subgraph SUBGRAPH_PARSED["デコードされた要素"]
HEADER_JSON["JWS Header (JSON)"] -->|"必須"| HEADER_ALG["alg: 署名アルゴリズム"]
HEADER_JSON -->|"任意"| HEADER_TYP["typ: トークンタイプ"]
PAYLOAD_JSON["JWS Payload (Claims)"] -->|"任意"| PAYLOAD_ISS["iss: 発行者"]
PAYLOAD_JSON -->|"任意"| PAYLOAD_SUB["sub: 主題"]
PAYLOAD_JSON -->|"任意"| PAYLOAD_AUD["aud: 受信者"]
PAYLOAD_JSON -->|"任意"| PAYLOAD_EXP["exp: 有効期限"]
PAYLOAD_JSON -->|"任意"| PAYLOAD_IAT["iat: 発行時間"]
PAYLOAD_JSON -->|"任意"| PAYLOAD_JTI["jti: JWT ID"]
SIGNATURE_BIN["JWS Signature (Bytes)"] -->|"アルゴリズムと鍵を使用"| SIGNATURE_VERIFY["署名検証"]
end
HEADER_B64 -->|"デコード"| HEADER_JSON
PAYLOAD_B64 -->|"デコード"| PAYLOAD_JSON
SIGNATURE_B64 -->|"デコード"| SIGNATURE_BIN
style HEADER_JSON fill:#f9f,stroke:#333,stroke-width:2px
style PAYLOAD_JSON fill:#bbf,stroke:#333,stroke-width:2px
style SIGNATURE_BIN fill:#cfc,stroke:#333,stroke-width:2pxヘッダ、ペイロード、署名の構造
JWTコンパクト表現: HeaderBase64URL.PayloadBase64URL.SignatureBase64URL JWS Header (JSON): alg: string (署名アルゴリズム, 例: HS256, RS256, RFC 7518で定義) typ: string (トークンタイプ, 通常 "JWT", 任意) kid: string (キーID, 鍵の特定に使用, 任意) ... (追加のヘッダパラメータ) JWS Payload (JSON Claims): iss: string (発行者 Issuer, RFC 7519 4.1.1) sub: string (主題 Subject, RFC 7519 4.1.2) aud: string (受信者 Audience, RFC 7519 4.1.3) exp: numeric (有効期限 Expiration Time, Unix時間, RFC 7519 4.1.4) nbf: numeric (使用可能開始時間 Not Before, Unix時間, RFC 7519 4.1.5) iat: numeric (発行時間 Issued At, Unix時間, RFC 7519 4.1.6) jti: string (JWT ID, 一意の識別子, RFC 7519 4.1.7) ... (カスタムクレーム, アプリケーション固有のデータ) JWS Signature: (JWS HeaderとJWS PayloadのBase64URLエンコード文字列を連結し、 指定されたアルゴリズム(alg)と秘密鍵/共有シークレットで署名したバイト列)
JWS Header:
JWTの署名に使用されるアルゴリズム (alg) やトークンタイプ (typ) などのメタデータをJSON形式で保持します。
JWS Payload (Claims): JWTの実質的な内容であり、認証情報や認可情報などの「クレーム」をJSON形式で保持します。クレームには以下の3種類があります。
登録済みクレーム (Registered Claims): RFC 7519で標準化されたクレーム(例:
iss,exp,sub)。これらは必須ではありませんが、相互運用性を高めます。公開クレーム (Public Claims): 衝突を避けるためにIANA JWT Registryに登録されているか、URIとして定義されるクレーム。
プライベートクレーム (Private Claims): 送信者と受信者の間で合意されたカスタムクレーム。
JWS Signature: JWS HeaderとJWS PayloadをBase64URLエンコードし、それらをドットで連結した文字列を、指定されたアルゴリズム(RFC 7518 JSON Web Algorithms [4] で定義)と秘密鍵または共有シークレットを用いて署名したものです。この署名により、JWTの完全性と信頼性が保証されます。
相互運用
JWTはJSONベースであり、コンパクトな表現形式を持つため、異なるシステム間での情報交換に適しています。標準化されたクレームセットと署名アルゴリズムにより、言語やプラットフォームが異なる環境でも容易に相互運用が可能です。
他の認証・認可プロトコルとの比較
SAML (Security Assertion Markup Language):
JWT: JSONベースで軽量。モバイルやシングルページアプリケーション (SPA) での利用に適しています。
SAML: XMLベースでより複雑。エンタープライズ環境でのシングルサインオン (SSO) に多く利用されます。SAMLはより多くのメタデータや機能を持つ反面、実装が複雑で、トークンサイズが大きくなる傾向があります。
セッションIDベースの認証:
JWT: サーバ側でセッション状態を持たないステートレス認証。水平スケーリングが容易で、データベース参照が不要なためサーバ負荷が低い。
セッションID: サーバ側でセッション状態を管理するステートフル認証。CSRF対策やセッション失効処理がサーバ側で容易。スケーラビリティにはロードバランサでのセッションアフィニティ維持などの工夫が必要。
セキュリティ考慮事項
JWTを安全に運用するためには、以下の点に留意する必要があります。
署名の検証の徹底:
JWTを受け取った側は、必ず署名を検証し、トークンが改ざんされていないこと、信頼できる発行者によって発行されたことを確認する必要があります。
ヘッダの
algパラメータで指定されたアルゴリズムを無条件に信頼せず、許可されたアルゴリズムのホワイトリストと照合することが重要です(「None」アルゴリズムへのダウングレード攻撃の防止)。
リプレイ攻撃対策:
exp(有効期限) クレームを必ず設定し、期限切れのトークンは拒否します。有効期限は短めに設定し、リフレッシュトークンと組み合わせて利用することが推奨されます。jti(JWT ID) クレームを利用して、一度使用されたトークンを記録し、再利用を防止するメカニズムを実装することも有効です。
キー更新(ローテーション):
- 署名に使用する秘密鍵や共有シークレットは定期的にローテーションする必要があります。JWKS (JSON Web Key Set) エンドポイントを利用して、公開鍵を安全に配布・管理する仕組みが一般的に利用されます。
0-RTTの再送リスクとJWT:
- JWT自体はトランスポート層の0-RTT (Zero Round Trip Time) とは直接関係ありませんが、初回認証後のリクエストにJWTを使用する際に、
expやjtiなどのクレーム検証が不十分だと、古いトークンの再送によるリプレイ攻撃のリスクが生じます。特に、JWTはステートレスであるため、一度漏洩したトークンは有効期限内であれば何度でも利用されうるため、上記の対策は必須です。
- JWT自体はトランスポート層の0-RTT (Zero Round Trip Time) とは直接関係ありませんが、初回認証後のリクエストにJWTを使用する際に、
クレームの厳格な検証:
iss(発行者)、aud(受信者)、sub(主題) などの登録済みクレームも、アプリケーションのコンテキストに合わせて厳格に検証する必要があります。例えば、audクレームが自サービスのものであるかを確認することで、意図しないサービスへのトークン利用を防ぎます。
機密情報の非格納:
- JWTのペイロードは署名されますが、暗号化されていない限り、Base64urlエンコードされているだけであり、誰でもデコードして内容を閲覧できます。したがって、ペイロードにはパスワードや個人情報などの機密情報を直接含めてはなりません。機密情報を保持する必要がある場合は、JWE (JSON Web Encryption) を利用してトークン全体を暗号化することを検討してください。
安全なトークン転送:
- JWTは、必ずHTTPS/TLSを介して転送し、盗聴を防ぐ必要があります。HTTP Only属性を持つCookieや、AuthorizationヘッダのBearerトークンとして送信するのが一般的です。XSS攻撃を防ぐため、JavaScriptから直接アクセスできないHTTP Only Cookieの使用が推奨されることがあります。
実装メモ
JWTを実装する際の注意点を以下に示します。
信頼できるライブラリの選択:
- JWTの生成・検証には、必ず標準に準拠した、セキュリティ監査済みのライブラリを使用してください。自分で暗号処理を実装することは避けるべきです。
適切な有効期限 (exp) の設定:
- 短すぎる有効期限はユーザー体験を損ない、長すぎる有効期限はトークン漏洩時のリスクを高めます。アプリケーションの要件とセキュリティリスクを考慮し、バランスの取れた有効期限を設定してください。通常、数分から数時間が推奨され、長期利用にはリフレッシュトークンとの組み合わせを検討します。
鍵管理の徹底:
署名鍵は安全な場所に保管し、アクセスを厳しく制限してください。本番環境ではHS256 (共有シークレット) ではなく、RS256/ES256 (公開鍵暗号) を使用し、秘密鍵が漏洩しないよう厳重に管理することが強く推奨されます。
JWKSエンドポイントの実装により、サービスが公開鍵を安全に提供できるようになります。
すべてのクレームの検証:
exp、nbf、iat、iss、aud、subなど、必要なすべてのクレームが有効であるかを確認するロジックを実装してください。特にexpは必須チェックです。
JWTサイズとネットワーク効率:
- JWTに多数のクレームや大きなデータを含めると、トークンサイズが増大します。これにより、HTTPヘッダのサイズが大きくなり、ネットワークのMTU (Maximum Transmission Unit) を超えることでパケットの断片化を引き起こす可能性があります。断片化はネットワーク性能を低下させ、信頼性に影響を与えるため、ペイロードは必要最小限に抑えるべきです。
パフォーマンスと処理オーバーヘッド:
- JWTの署名検証は計算資源を消費します。特に高負荷環境では、検証処理がボトルネックにならないよう、適切なキャッシング戦略(例: 公開鍵のキャッシング)や、検証済みのトークンを一時的にメモリに保持するなどの工夫が必要です。
JWT発行・検証シーケンス
sequenceDiagram
actor User
participant Client
participant AuthorizationServer as AS
participant ResourceServer as RS
User->>Client: 認証リクエスト (例: ログイン)
Client->>AS: |認証情報提示| (例: ユーザー名/パスワード)
AS->>AS: ユーザー認証と認可処理
AS->>AS: JWT生成 (Header, Payload, 秘密鍵で署名)
AS-->>Client: |JWT発行| アクセストークン (JWT)
Client->>RS: |JWT提示| HTTP Authorization: Bearer
RS->>RS: JWT検証プロセス開始
RS->>RS: 1. JWT構造のチェック (Base64urlエンコード)
RS->>RS: 2. Header/Payloadのデコード
RS->>RS: 3. Headerの'alg'と秘密鍵/公開鍵を使用して署名検証
RS->>RS: 4. Payloadのクレーム検証 ('exp', 'nbf', 'iss', 'aud', 'jti'など)
alt 検証成功
RS-->>Client: リソース提供 (200 OK)
else 検証失敗
RS-->>Client: 認可エラー (401 Unauthorized)
endまとめ
RFC 7519によって定義されるJSON Web Token (JWT) は、WebアプリケーションやAPIにおけるステートレスな認証・認可を実現するための強力なツールです。コンパクトで自己完結型、そして暗号学的に検証可能な特性により、分散システムのスケーラビリティとセキュリティ向上に貢献します。
しかし、その利点を最大限に活かすためには、署名検証の徹底、適切な有効期限とリプレイ対策、鍵の厳重な管理、そして機密情報をトークンに含めないといったセキュリティ上の考慮事項を十分に理解し、正しく実装することが不可欠です。適切なライブラリの選択と堅牢な検証ロジックにより、JWTを安全かつ効率的にシステムに組み込むことができます。
参考文献
[1] RFC 7519, “JSON Web Token (JWT)”, IETF, 2015年5月発行. (URL:
https://datatracker.ietf.org/doc/html/rfc7519)[2] RFC 7515, “JSON Web Signature (JWS)”, IETF, 2015年5月発行. (URL:
https://datatracker.ietf.org/doc/html/rfc7515)[3] RFC 7516, “JSON Web Encryption (JWE)”, IETF, 2015年5月発行. (URL:
https://datatracker.ietf.org/doc/html/rfc7516)[4] RFC 7518, “JSON Web Algorithms (JWA)”, IETF, 2015年5月発行. (URL:
https://datatracker.ietf.org/doc/html/rfc7518)[5] OWASP Cheat Sheet Series, “JSON Web Token Cheat Sheet”, OWASP Foundation, 最終更新日: 2024年3月28日. (URL:
https://cheatsheetseries.owasp.org/cheatsheets/JSON_Web_Token_Cheat_Sheet.html)

