JWT入門 - JSON Web Tokenの構造・署名・使い方と注意点
JWT(JSON Web Token)は、JSON形式の情報をコンパクトに表現するためのトークン形式。主に認証・認可の場面でサーバーとクライアント間の情報伝達に使われる。
JWTの構造
JWTは . で区切られた3つのBase64URLエンコードされた文字列で構成される。
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IlRhcm8gU2F0byIsImlhdCI6MTcxMTg4NjQwMH0.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
[ヘッダー].[ペイロード].[署名]
ヘッダー(Header)
署名アルゴリズムとトークンタイプを指定する。
{
"alg": "HS256",
"typ": "JWT"
}
| フィールド | 説明 |
|---|---|
alg | 署名アルゴリズム(HS256, RS256など) |
typ | トークンタイプ(通常 JWT) |
ペイロード(Payload)
実際のデータ(クレーム)を格納する。
{
"sub": "1234567890",
"name": "佐藤太郎",
"email": "taro@example.com",
"role": "admin",
"iat": 1711886400,
"exp": 1711890000
}
標準クレーム(Registered Claims)
| クレーム | 名称 | 説明 |
|---|---|---|
iss | Issuer | 発行者 |
sub | Subject | 主体(通常はユーザーID) |
aud | Audience | 想定受信者 |
exp | Expiration Time | 有効期限(Unixタイムスタンプ) |
nbf | Not Before | この時刻より前は無効 |
iat | Issued At | 発行時刻 |
jti | JWT ID | JWT固有のID |
ペイロードはBase64URLエンコードされているだけで暗号化されていない。誰でもデコードして内容を読める。機密情報は格納しない。
署名(Signature)
ヘッダーとペイロードの改ざんを検証するための署名。
HMACSHA256(
base64UrlEncode(header) + "." + base64UrlEncode(payload),
secret
)
秘密鍵(または公開鍵ペア)を使って生成するため、サーバーだけが有効な署名を作れる。サーバーは受け取ったJWTの署名を再計算して一致するか検証する。
署名アルゴリズム
対称アルゴリズム(HMAC)
同じ秘密鍵で署名と検証を行う。発行者と検証者が同じシステムの場合に適している。
| アルゴリズム | 説明 |
|---|---|
HS256 | HMAC + SHA-256(最も一般的) |
HS384 | HMAC + SHA-384 |
HS512 | HMAC + SHA-512 |
非対称アルゴリズム(RSA / ECDSA)
秘密鍵で署名し、公開鍵で検証する。認可サーバーとリソースサーバーが別の場合(OIDCなど)に適している。
| アルゴリズム | 説明 |
|---|---|
RS256 | RSA + SHA-256 |
ES256 | ECDSA + SHA-256(RSAより鍵が短く高速) |
PS256 | RSA-PSS + SHA-256 |
外部サービスのJWTを検証する場合(例: Google OIDCトークン)は、そのサービスが公開する公開鍵を使って検証する。
認証フロー
JWTを使った典型的な認証フローはBearer認証。
sequenceDiagram
participant C as クライアント
participant S as サーバー
C->>S: POST /auth/login
Note over C,S: { email, password }
S-->>C: 200 OK
Note over C,S: { token: "eyJ..." }
C->>S: GET /api/profile
Note over C,S: Authorization: Bearer eyJ...
Note over S: 1. トークンを取り出す<br/>2. 署名を検証する<br/>3. 有効期限を確認する<br/>4. claimを使って処理
S-->>C: 200 OK
Note over C,S: { name: "佐藤太郎", ... }
実装例
Node.js(jsonwebtoken)
const jwt = require('jsonwebtoken')
const SECRET = process.env.JWT_SECRET
// トークン生成
const token = jwt.sign(
{ sub: user.id, email: user.email, role: user.role },
SECRET,
{ expiresIn: '1h' }
)
// トークン検証
try {
const payload = jwt.verify(token, SECRET)
console.log(payload.sub) // ユーザーID
} catch (err) {
if (err instanceof jwt.TokenExpiredError) {
// 有効期限切れ
} else if (err instanceof jwt.JsonWebTokenError) {
// 不正なトークン
}
}
Python(PyJWT)
import jwt
from datetime import datetime, timedelta, timezone
SECRET = "your-secret-key"
# トークン生成
payload = {
"sub": str(user.id),
"email": user.email,
"exp": datetime.now(timezone.utc) + timedelta(hours=1),
}
token = jwt.encode(payload, SECRET, algorithm="HS256")
# トークン検証
try:
payload = jwt.decode(token, SECRET, algorithms=["HS256"])
except jwt.ExpiredSignatureError:
pass # 有効期限切れ
except jwt.InvalidTokenError:
pass # 不正なトークン
トークンの保存場所
| 保存場所 | メリット | デメリット |
|---|---|---|
| localStorage | シンプルで扱いやすい | XSSで盗まれるリスク |
| SessionStorage | タブを閉じると消える | XSSで盗まれるリスク |
| HttpOnly Cookie | JSからアクセス不可 | CSRF対策が必要 |
セキュリティを重視する場合は HttpOnly + Secure + SameSite=Strict の Cookie に保存するのが推奨される。
リフレッシュトークン
アクセストークンの有効期限を短く(15〜60分)設定し、長期間有効なリフレッシュトークンで再発行する構成が一般的。
1. ログイン → アクセストークン(15分)+ リフレッシュトークン(7日)を発行
2. APIアクセス時 → アクセストークンをAuthorizationヘッダーに付与
3. アクセストークン期限切れ → リフレッシュトークンで新しいアクセストークンを取得
4. リフレッシュトークン期限切れ → 再ログイン
セキュリティ上の注意点
alg: none 攻撃
alg フィールドを none にすることで署名検証をスキップさせる攻撃。ライブラリによっては脆弱性があった。
対策: 検証時に許可するアルゴリズムを明示的に指定する。
jwt.verify(token, SECRET, { algorithms: ['HS256'] })
機密情報の格納
ペイロードはBase64URLで可逆的にデコードできる。パスワード、クレジットカード番号などは格納しない。
有効期限の設定
無期限のJWTは一度漏洩すると永遠に悪用される。exp クレームは必ず設定する。
トークンの無効化
JWTはステートレスなため、サーバー側で発行済みトークンを一方的に無効化できない。ログアウト時の無効化には次のいずれかが必要。
- ブラックリスト: 無効にしたい
jtiをDBに保存して検証時にチェック - 有効期限を短くする: 漏洩しても被害期間を最小化
- リフレッシュトークンの破棄: リフレッシュトークンを無効化して更新不可にする
アルゴリズムの強度
HS256 の秘密鍵は十分な長さ(256bit以上)のランダム文字列を使用する。短い文字列や推測可能な文字列は総当たり攻撃に弱い。
JWTとセッションの比較
| 項目 | JWT | セッション |
|---|---|---|
| 状態管理 | ステートレス(サーバー不要) | ステートフル(サーバーにセッション保存) |
| スケーラビリティ | 高い(DBアクセス不要) | サーバー間でセッション共有が必要 |
| 無効化 | 困難(有効期限まで有効) | 即時無効化が容易 |
| サイズ | 大きい(数百バイト) | 小さい(セッションIDのみ) |
| 向いている用途 | マイクロサービス、API間認証 | 従来のWebアプリ |
参考リンク
- RFC 7519 - JSON Web Token (JWT) - IETF公式仕様
- RFC 7515 - JSON Web Signature (JWS) - 署名の仕様
- jwt.io - JWTのデコード・デバッグツール(公式)