Web APIの認証方式として広く採用されているJWT(JSON Web Token)は、サーバー側でセッション状態を保持しなくてもユーザーを識別できるトークン形式です。読み方は「ジョット」で、RFC 7519で標準化されています。
JWTはヘッダー・ペイロード・署名の3パートをドット(.)で結合した文字列であり、Base64URLエンコードされたJSON情報と改ざん検知用の署名を1つのトークンにまとめています。
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
上の文字列がJWTの実物です。一見するとランダムな文字列に見えますが、ドットで区切られた3つのパートそれぞれに明確な役割があります。

JWTを構成する3つのパート
ヘッダー(Header)
ヘッダーはトークンのメタ情報を格納するJSONオブジェクトです。署名に使うアルゴリズムの種別(alg)とトークンの種類(typ)を宣言します。
{
"alg": "HS256",
"typ": "JWT"
}
このJSONをBase64URLエンコードすると、トークンの最初のパート eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9 になります。
代表的な署名アルゴリズムは以下の通りです。
| アルゴリズム | 方式 | 鍵の種類 | 用途 |
|---|---|---|---|
| HS256 | HMAC + SHA-256 | 共通鍵(対称鍵) | 単一サーバー構成 |
| RS256 | RSA + SHA-256 | 公開鍵/秘密鍵(非対称鍵) | マイクロサービス構成 |
| ES256 | ECDSA + P-256 + SHA-256 | 公開鍵/秘密鍵(非対称鍵) | モバイル・IoT向け |
HS256は発行と検証の両方で同じ鍵を使うため構成が単純です。RS256やES256は署名に秘密鍵、検証に公開鍵を使う非対称鍵方式で、検証側に秘密鍵を渡す必要がなく、複数サービスでの検証に適しています。
ペイロード(Payload)
ペイロードは「クレーム(Claims)」と呼ばれるキーバリューの組を格納するJSONオブジェクトです。ユーザーIDや有効期限など、トークンに埋め込みたい情報を記述します。
{
"sub": "user-12345",
"name": "Taro Yamada",
"role": "admin",
"iat": 1740000000,
"exp": 1740003600
}
クレームは3種類に分類されます。
登録済みクレーム(Registered Claims)
RFC 7519で定義された標準クレームです。すべて3文字の省略名が割り当てられています。
| クレーム | 正式名 | 説明 |
|---|---|---|
iss | Issuer | トークン発行者 |
sub | Subject | トークンの主体(ユーザーIDなど) |
aud | Audience | トークンの受信対象 |
exp | Expiration Time | 有効期限(UNIXタイムスタンプ) |
nbf | Not Before | 有効開始日時 |
iat | Issued At | 発行日時 |
jti | JWT ID | トークンの一意識別子(リプレイ攻撃防止用) |
パブリッククレーム(Public Claims)
IANAのJSON Web Token Claims Registryに登録された名前、またはURIで名前空間を区別したクレームです。
プライベートクレーム(Private Claims)
発行者と受信者の間で合意した独自のクレームです。roleやnameのように、アプリケーション固有の情報を格納します。
ペイロードはBase64URLエンコードされているだけで暗号化されていません。Base64URLデコードすれば誰でも中身を読めます。パスワードやクレジットカード番号など、秘匿すべき情報は絶対にペイロードに含めないでください。
署名(Signature)
署名はトークンの改ざんを検知するためのパートです。ヘッダーとペイロードをドットで結合した文字列に対し、指定されたアルゴリズムと秘密鍵で署名を生成します。
HS256の場合、署名の生成と検証は次の流れで行われます。

署名が付与されることで、トークンの受信側は「ヘッダーやペイロードが途中で書き換えられていないか」を検証できます。署名はあくまで**完全性(改ざん検知)**を保証するものであり、中身の秘匿(暗号化)は行いません。ペイロードの暗号化が必要な場合はJWE(JSON Web Encryption)という別の仕様を使います。
Base64URLエンコードとは
JWTで使われるBase64URLエンコードは、標準的なBase64エンコードの派生形式です。HTTPのURLやヘッダーに安全に含められるよう、以下の置換を行います。
| 標準Base64 | Base64URL |
|---|---|
+ | - |
/ | _ |
末尾の=パディング | 削除 |
この変換により、JWTはURLのクエリパラメータやHTTPヘッダーにそのまま埋め込めます。
JWT認証の全体フロー
JWT認証は、クライアント(ブラウザやモバイルアプリ)とサーバーの間で次のように動作します。

この図のポイントは、ステップ4の検証でサーバーがデータベースに問い合わせていない点です。JWT自体にユーザー情報と署名が含まれているため、トークンの正当性をサーバー単体で判断できます。これがJWTの「ステートレス」という特徴です。
1. ログインリクエスト
クライアントがユーザーIDとパスワードをサーバーに送信します。
POST /api/login HTTP/1.1
Content-Type: application/json
{
"email": "taro@example.com",
"password": "s3cureP@ss"
}
2. トークン発行
サーバーは認証情報をデータベースと照合し、正しければJWTを生成してクライアントに返します。
HTTP/1.1 200 OK
Content-Type: application/json
{
"access_token": "eyJhbGciOiJIUzI1NiIs...",
"token_type": "Bearer",
"expires_in": 3600
}
3. トークン付きリクエスト
クライアントは受け取ったJWTを保存し、以降のAPIリクエストでAuthorizationヘッダーに付与します。
GET /api/profile HTTP/1.1
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...
4. トークン検証
サーバーは受信したJWTに対し、以下の順で検証を行います。
- 署名検証 — 秘密鍵(HS256)または公開鍵(RS256/ES256)を使い、ヘッダー+ペイロードから署名を再計算してトークンの署名と一致するか確認
- 有効期限チェック —
expクレームが現在時刻より未来であることを確認 - 発行者チェック —
issクレームが期待する発行者と一致するか確認 - 対象者チェック —
audクレームが自サービスを指しているか確認
すべて通過すれば、ペイロードからユーザー情報を取り出してリクエストを処理します。
共通鍵方式と公開鍵方式の違い
JWT署名には大きく2つのアプローチがあります。どちらを選ぶかはシステム構成によって決まります。
共通鍵方式(HMAC:HS256)
発行側と検証側が同じ秘密鍵を共有します。
- トークンを発行するサーバーと検証するサーバーが同一、または少数の場合に適しています
- 秘密鍵が外部に漏れると、第三者が任意のトークンを偽造できます
- 計算コストが低く高速です
公開鍵方式(RSA / ECDSA:RS256 / ES256)
発行側が秘密鍵で署名し、検証側が公開鍵で検証します。
- マイクロサービス構成で各サービスが独立して検証する場合に適しています
- 検証側には公開鍵だけを配布すればよく、秘密鍵の共有が不要です
- OIDCプロバイダー(Auth0、Keycloakなど)は公開鍵をJWKS(JSON Web Key Set)エンドポイントで配布するのが一般的です
- 計算コストはHMACより高くなります
| 比較項目 | 共通鍵(HS256) | 公開鍵(RS256 / ES256) |
|---|---|---|
| 鍵の管理 | 発行側・検証側で同一鍵を共有 | 秘密鍵は発行側のみ、公開鍵を配布 |
| 検証サーバーの追加 | 鍵を安全に共有する手間が増加 | 公開鍵を渡すだけ |
| 偽造リスク | 検証側も秘密鍵を持つため偽造可能 | 検証側は署名を作れない |
| 処理速度 | 高速 | やや遅い |
| 適するアーキテクチャ | モノリシック・小規模 | マイクロサービス・大規模 |
セッション認証とJWT認証の使い分け
セッション認証とJWT認証はどちらも「ログイン済みユーザーを識別する」ための仕組みですが、状態管理の方法が根本的に異なります。
| 比較項目 | セッション認証 | JWT認証 |
|---|---|---|
| 状態の保持場所 | サーバー側(セッションストア) | クライアント側(トークン内) |
| スケールアウト | セッション共有が必要(Redis等) | サーバー間共有不要 |
| 即座のログアウト | セッション破棄で即反映 | トークン無効化が困難 |
| 通信間のデータ量 | セッションIDのみ(小さい) | トークン全体(やや大きい) |
| CSRF対策 | 必要(Cookieベース) | 不要(Authorizationヘッダーの場合) |
| 適するケース | 従来のWebアプリ、即時ログアウト重視 | SPA、モバイルアプリ、API間連携 |
JWT認証はステートレスであるため水平スケーリングに強い反面、トークン発行後に即座に無効化する手段が限られます。強制ログアウトやアカウント停止の即時反映が求められるシステムでは、セッション認証を選ぶか、JWTにブラックリスト機構を組み合わせる設計が必要です。
JWT vs APIキー:認証方式の選定基準
APIの認証方式として、JWTとAPIキーの比較も押さえておくと実務で役立ちます。米国ではこの2つの使い分けが頻繁に議論されています。
| 比較項目 | JWT | APIキー |
|---|---|---|
| 認証の単位 | ユーザー単位 | アプリケーション単位 |
| 有効期限 | 短時間(数分〜数時間) | 長期(手動ローテーション) |
| 含まれる情報 | ユーザーID、権限、有効期限など | 識別子のみ |
| 無効化 | 有効期限切れまで原則有効 | サーバー側で即時無効化可能 |
| 適する用途 | ユーザー認証、権限制御 | サーバー間通信、サードパーティAPI連携 |
ユーザーごとにアクセス権限を細かく制御する場面ではJWTが適しています。一方、外部サービスにAPIを公開して利用量を管理する場面ではAPIキーが適しています。両方を組み合わせる設計(APIキーでアプリケーション認証 + JWTでユーザー認証)も実務では一般的です。
JWTのセキュリティ対策と既知の脆弱性
JWTを安全に運用するには、仕様上の注意点と実装時の落とし穴を把握しておく必要があります。
alg: none 攻撃
JWT仕様はアルゴリズムにnoneを指定することを許容しています。攻撃者がヘッダーのalgをnoneに書き換え、署名パートを空にしたトークンを送信すると、検証が甘い実装ではそのまま受け入れてしまいます。
対策: サーバー側で受け入れるアルゴリズムをホワイトリストで明示的に指定し、noneを拒否します。
アルゴリズム混同攻撃
RS256(公開鍵方式)で検証するサーバーに対し、攻撃者がヘッダーのalgをHS256に書き換え、公開鍵を共通鍵として署名したトークンを送信する手法です。サーバーがヘッダーのalgを無条件に信頼すると、公開鍵(公開情報)で署名検証が通ってしまいます。
対策: 検証時にヘッダーのalg値を使わず、サーバー側で使用するアルゴリズムを固定します。
トークン漏えいとハイジャック
JWTは一度発行されると有効期限まで無効化できないため、漏えいした場合の影響が大きくなります。
対策:
- 通信は必ずHTTPS経由で行う
exp(有効期限)を短く設定する(15分〜1時間程度)- 短命なアクセストークンと長命なリフレッシュトークンを組み合わせる
- ブラウザでの保存先は
HttpOnly属性付きCookieを推奨(localStorageはXSS攻撃で読み取られるリスクがある)
ペイロードの情報漏えい
ペイロードはBase64URLエンコードされているだけで暗号化されていません。トークンを入手すれば誰でも中身を読めます。
対策: 機密情報をペイロードに含めない。暗号化が必要な場合はJWE(JSON Web Encryption、RFC 7516)を使用します。
リフレッシュトークンの設計パターン
アクセストークンの有効期限を短くするとセキュリティは向上しますが、ユーザーが頻繁に再ログインを求められるためUXが低下します。この問題を解決するのがリフレッシュトークンです。

リフレッシュトークンはサーバー側のデータベースに保存し、無効化可能な状態を維持します。アカウント停止や強制ログアウトの際は、リフレッシュトークンをサーバー側で削除することでトークンの更新を防ぎます。
リファレンストークンという代替手段
JWTはトークン自体にすべての情報を含む「自己完結型トークン(Self-contained Token)」です。一方、「リファレンストークン(Opaque Token)」は単なるランダムな識別子で、実際のユーザー情報はサーバー側のデータベースに格納します。
| 比較項目 | JWT(自己完結型) | リファレンストークン |
|---|---|---|
| トークンのサイズ | 大きい(数百バイト) | 小さい(識別子のみ) |
| サーバー問い合わせ | 不要(署名検証のみ) | 毎回データベース参照が必要 |
| 即時無効化 | 困難 | データベース削除で即時反映 |
| ステートレス性 | ステートレス | ステートフル |
| 情報漏えいリスク | ペイロードが読めてしまう | トークンから情報を読み取れない |
IdentityServer(Duende Software)やKeycloakなどの認証基盤では、JWTとリファレンストークンの切り替えが可能です。即時のトークン失効が必要なシステムでは、リファレンストークンやJWTにブラックリストを組み合わせる設計を検討してください。
JWTに関連する仕様と技術
JWTは単体の仕様ではなく、複数のRFCで構成されるJOSE(JSON Object Signing and Encryption)ファミリーの一部です。
| 仕様 | RFC | 役割 |
|---|---|---|
| JWT | RFC 7519 | クレームをJSON形式で表現するトークンの書式 |
| JWS | RFC 7515 | JSONデータに署名を付与する方式 |
| JWE | RFC 7516 | JSONデータを暗号化する方式 |
| JWK | RFC 7517 | 暗号鍵をJSON形式で表現する方式 |
| JWA | RFC 7518 | 使用可能なアルゴリズムの定義 |
一般に「JWT」と呼ばれるものは、厳密にはJWT + JWS(署名付きJWT)です。ペイロードの暗号化が必要な場合はJWT + JWE(暗号化JWT)となります。
JWTはOAuth 2.0やOpenID Connect(OIDC)で中核的な役割を果たしています。OIDCのIDトークンはJWT形式で発行され、シングルサインオン(SSO)の実現にも利用されています。
よくある疑問
JWTは暗号化されていますか?
標準的なJWT(JWS形式)は署名付きですが暗号化はされていません。Base64URLエンコードは可逆的な変換であり、暗号化ではありません。ペイロードの暗号化が必要な場合はJWEを使用します。
セッション管理にJWTを使ってもよいですか?
技術的には可能ですが、JWTをセッション管理に使うことへの批判も存在します。JWTは有効期限まで無効化が困難であるため、即座のログアウトやセッション取り消しが求められるシステムには向きません。JWTはAPI認証やサービス間連携での使用が最も適しています。
JWTの有効期限はどのくらいが適切ですか?
アクセストークンは15分〜1時間が一般的です。有効期限が短いほどトークン漏えいの影響を限定できます。リフレッシュトークンは7日〜30日程度に設定し、サーバー側で無効化できるようにします。
JWTのデコードと検証の違いは?
デコードはBase64URLエンコードされた文字列を元のJSONに変換する操作で、鍵がなくても実行できます。検証は署名の正当性・有効期限・発行者などを確認する操作で、秘密鍵または公開鍵が必要です。JWTを受信した側は必ず検証を行ってください。デコードだけでは改ざんの有無を確認できません。
JWTはどこに保存するのが安全ですか?
ブラウザ環境ではHttpOnly・Secure・SameSite属性を設定したCookieに保存することが推奨されます。localStorageはJavaScriptからアクセス可能なためXSS攻撃で漏えいするリスクがあります。モバイルアプリではOSが提供するセキュアストレージ(iOSのKeychain、AndroidのKeyStore)を使用します。
まとめ
JWTはヘッダー・ペイロード・署名の3パート構造で、ステートレスな認証を実現するトークン形式です。セッション認証と異なりサーバー側に状態を持たないため、水平スケーリングやマイクロサービス構成に適しています。
一方で、即時のトークン無効化が困難、ペイロードが暗号化されないといった制約もあります。リフレッシュトークンの導入、短い有効期限の設定、HTTPS通信の徹底、保存場所の選定など、セキュリティ対策を組み合わせて運用することが重要です。
