#はじめに
「APIの認証にJWTを使っています」——よく聞くフレーズですが、JWTとは何で、なぜ使われるのでしょうか?
前回学んだサーバーサイドセッションは、セッションデータをサーバーに保存していました。JWTは異なるアプローチで、トークン自体に情報を含めます。これにより、サーバー側でセッションストレージを持たなくても認証が可能になります。
この記事を読むと、以下のことができるようになります:
- JWTの構造と仕組みを理解できる
- 署名検証の役割がわかる
- JWTの保存場所の議論を理解し、適切な選択ができる
- リフレッシュトークンの役割がわかる
#JWTとは
JWT(JSON Web Token、「ジョット」と読む)とは、JSON形式の情報を安全にやり取りするためのトークン形式です。
見た目は長い文字列です:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IuWxseeUsOWkqumDjiIsImlhdCI6MTUxNjIzOTAyMn0.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
ピリオド.で区切られた3つのパートで構成されています。
#JWTの構造
ヘッダー.ペイロード.署名
(Header).(Payload).(Signature)
#1. ヘッダー(Header)
トークンのメタ情報(アルゴリズム、タイプ)をBase64URLエンコードしたものです。
{
"alg": "HS256",
"typ": "JWT"
}
→ eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
#2. ペイロード(Payload)
実際に伝えたい情報(クレーム)をBase64URLエンコードしたものです。
{
"sub": "1234567890",
"name": "山田太郎",
"iat": 1516239022,
"exp": 1516242622
}
→ eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IuWxseeUsOWkqumDjiIsImlhdCI6MTUxNjIzOTAyMn0
#標準クレーム
| クレーム | 説明 |
|---|---|
sub (Subject) | トークンの主体(ユーザーID等) |
iat (Issued At) | 発行日時(UNIXタイムスタンプ) |
exp (Expiration) | 有効期限 |
iss (Issuer) | 発行者 |
aud (Audience) | 対象者 |
#3. 署名(Signature)
ヘッダーとペイロードを結合し、秘密鍵で署名したものです。
HMACSHA256(
base64UrlEncode(header) + "." + base64UrlEncode(payload),
secret
)
→ SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
#なぜ署名が必要なのか
ペイロードはBase64URLエンコードされているだけで、暗号化されていません。誰でもデコードして内容を見ることができます。
// ペイロードのデコード(誰でもできる)
atob('eyJzdWIiOiIxMjM0NTY3ODkwIn0');
// → {"sub":"1234567890"}
では、なぜ安全なのか? それは署名があるからです。
#署名の役割
- 改ざん検知: ペイロードを書き換えると、署名が一致しなくなる
- 発行者の証明: 秘密鍵を知っている人しか有効な署名を作れない
攻撃者が role: "admin" に書き換えても...
→ 署名を再計算できない(秘密鍵を知らない)
→ サーバーが署名検証で拒否
#署名アルゴリズム
| アルゴリズム | 種類 | 説明 |
|---|---|---|
| HS256 | 対称鍵 | 共有秘密鍵で署名・検証 |
| RS256 | 非対称鍵 | 秘密鍵で署名、公開鍵で検証 |
| ES256 | 非対称鍵 | 楕円曲線暗号、RS256より短い鍵 |
マイクロサービス間や外部サービスとの連携では、非対称鍵(RS256、ES256)が推奨されます。
#JWTの認証フロー
[クライアント] [サーバー]
| |
|------ ログイン情報 ------------------->|
| |
| 認証成功 → JWT生成
| |
|<----- JWT (アクセストークン) ----------|
| |
|------ Authorization: Bearer {JWT} ---->|
| |
| 署名検証 → ペイロード取得
| |
|<----- レスポンス ----------------------|
サーバーはJWTの署名を検証し、ペイロードからユーザー情報を取得します。セッションストレージへのアクセスは不要です。
#サーバーサイドセッションとの比較
| 観点 | サーバーサイドセッション | JWT |
|---|---|---|
| データ保存場所 | サーバー | トークン自体 |
| スケーラビリティ | セッション共有が必要 | ステートレス、共有不要 |
| 即時無効化 | 可能 | 困難 |
| トークンサイズ | 小さい(IDのみ) | 大きい(情報を含む) |
| セキュリティ | データはサーバーで保護 | ペイロードは見られる |
#JWTの保存場所
JWTをクライアントのどこに保存するかは、議論の多いトピックです。
#選択肢1: localStorage
// 保存
localStorage.setItem('token', jwt);
// 取得
const token = localStorage.getItem('token');
// リクエストに付与
fetch('/api/data', {
headers: {
'Authorization': `Bearer ${token}`
}
});
| メリット | デメリット |
|---|---|
| 実装が簡単 | XSSで盗まれる |
| CSRFの影響を受けない | JavaScriptからアクセス可能 |
#選択肢2: HttpOnly Cookie
Set-Cookie: token=eyJhbGci...; HttpOnly; Secure; SameSite=Strict
| メリット | デメリット |
|---|---|
| XSSで盗まれない | CSRFの考慮が必要 |
| JavaScriptからアクセス不可 | Cookie送信の制約 |
#推奨: HttpOnly Cookie
セキュリティを重視する場合、HttpOnly Cookieが推奨されます。XSS脆弱性があっても、トークンを盗まれるリスクを軽減できます。
Set-Cookie: access_token=eyJhbGci...; HttpOnly; Secure; SameSite=Strict; Path=/
#リフレッシュトークン
JWTは一度発行されると、有効期限まで無効化できません。そのため、有効期限を短く設定し、リフレッシュトークンで更新する方式が一般的です。
#2種類のトークン
| トークン | 有効期限 | 用途 |
|---|---|---|
| アクセストークン | 短い(5分〜1時間) | APIアクセス |
| リフレッシュトークン | 長い(数日〜数週間) | アクセストークンの更新 |
#更新フロー
[クライアント] [サーバー]
| |
|------ API呼び出し (アクセストークン) --->|
| |
|<----- 401 Unauthorized (期限切れ) ------|
| |
|------ リフレッシュトークン送信 ---------->|
| |
|<----- 新しいアクセストークン -----------|
| |
|------ API再呼び出し (新トークン) ------->|
|<----- 成功 -----------------------------|
#リフレッシュトークンの保存
リフレッシュトークンは長期間有効なため、より安全に保管する必要があります。
# アクセストークン: メモリまたは短命Cookie
# リフレッシュトークン: HttpOnly Cookie(別パス)
Set-Cookie: refresh_token=xyz...; HttpOnly; Secure; SameSite=Strict; Path=/auth/refresh
#JWTの注意点
#1. 機密情報を入れない
ペイロードは誰でも読めます。パスワードや個人情報を含めないでください。
// ❌ 危険
{
"email": "user@example.com",
"password": "secret123"
}
// ✅ OK
{
"sub": "user_123",
"role": "admin"
}
#2. 有効期限を適切に設定
長すぎる有効期限は、トークン漏洩時のリスクを高めます。
{
"exp": 1700000000, // 適切な有効期限を設定
"iat": 1699999000
}
#3. 署名アルゴリズムの検証
サーバーは、受け取ったJWTのアルゴリズムを信用してはいけません。
// 攻撃: alg を none に変更
{
"alg": "none", // 署名なし!
"typ": "JWT"
}
サーバー側で許可するアルゴリズムを明示的に指定してください。
#4. 即時無効化が必要な場合
JWTは即時無効化が困難です。以下の対策を検討してください:
- 有効期限を短くする
- ブラックリスト(無効化したトークンのリスト)を管理
- 重要な操作では追加の認証を要求
#まとめ
- JWTはヘッダー、ペイロード、署名の3部構成
- ペイロードは暗号化されていない(誰でも読める)
- 署名で改ざんを検知、秘密鍵なしに有効なJWTは作れない
- 保存場所はHttpOnly Cookieが推奨
- リフレッシュトークンで短い有効期限のアクセストークンを更新
- 機密情報をペイロードに含めない
#次のステップ
JWTの基礎を理解したところで、次はOAuth 2.0とOIDCについて学びましょう。「Googleでログイン」「GitHubでログイン」——これらのソーシャルログインがどのように実現されているのかがわかります。
#参考リンク
- JWT.io - JWTのデコード・検証ツール
- RFC 7519 - JSON Web Token
- OWASP - JSON Web Token Cheat Sheet