この用語をシェア
JWTとは
JWT(JSON Web Token)は、安全にユーザー情報を送信するためのコンパクトで自己記述的なトークンです。RFC 7519として標準化されており、認証とデータ交換において広く使用されています。JWTは、情報をJSONオブジェクトとして安全に送信でき、デジタル署名によって検証可能です。
JWTの主要な特徴
- コンパクト:URLセーフなBase64エンコーディング
- 自己記述的:必要な情報がトークン内に含まれる
- 検証可能:デジタル署名による完全性の保証
- ステートレス:サーバーで状態を保持する必要がない
- 相互運用性:異なるドメイン間でも使用可能
JWTの構造
JWTは、ピリオド(.)で区切られた3つの部分から構成されます:
Header.Payload.Signature
完全なJWTの例
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IuOCv+ODg+OBquKAnEpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
Headerの詳細
ヘッダーには、トークンの型と署名アルゴリズムが含まれます。
Header例
{
"alg": "HS256",
"typ": "JWT"
}
主要なアルゴリズム
アルゴリズム | 説明 | 用途 |
---|---|---|
HS256 | HMAC with SHA-256 | 対称鍵署名 |
RS256 | RSA Signature with SHA-256 | 非対称鍵署名 |
ES256 | ECDSA with SHA-256 | 楕円曲線デジタル署名 |
none | 署名なし | 非本番環境のみ |
Payloadの詳細
ペイロードには、クレーム(claim)と呼ばれる情報が含まれます。
Payload例
{
"sub": "1234567890",
"name": "田中太郎",
"email": "tanaka@example.com",
"iat": 1516239022,
"exp": 1516325422,
"iss": "https://example.com",
"aud": "https://api.example.com"
}
標準クレーム
クレーム | 説明 | 例 |
---|---|---|
iss | Issuer(発行者) | "https://example.com" |
sub | Subject(主体) | "1234567890" |
aud | Audience(対象者) | "https://api.example.com" |
exp | Expiration Time(有効期限) | 1516325422 |
iat | Issued At(発行時刻) | 1516239022 |
nbf | Not Before(有効開始時刻) | 1516239022 |
jti | JWT ID(一意識別子) | "abc123" |
Signatureの詳細
署名は、ヘッダーとペイロードの改ざんを検証するために使用されます。
HMAC SHA256での署名生成
HMACSHA256(
base64UrlEncode(header) + "." +
base64UrlEncode(payload),
secret
)
RSA SHA256での署名生成
RSASHA256(
base64UrlEncode(header) + "." +
base64UrlEncode(payload),
privateKey
)
JWTの実装例
Node.js(jsonwebtoken)
const jwt = require('jsonwebtoken');
// JWT生成
const payload = {
sub: '1234567890',
name: '田中太郎',
email: 'tanaka@example.com',
iat: Math.floor(Date.now() / 1000)
};
const secret = 'your-secret-key';
const token = jwt.sign(payload, secret, { expiresIn: '1h' });
console.log(token);
// JWT検証
try {
const decoded = jwt.verify(token, secret);
console.log(decoded);
} catch (err) {
console.error('Invalid token:', err.message);
}
Python(PyJWT)
import jwt
import datetime
# JWT生成
payload = {
'sub': '1234567890',
'name': '田中太郎',
'email': 'tanaka@example.com',
'iat': datetime.datetime.utcnow(),
'exp': datetime.datetime.utcnow() + datetime.timedelta(hours=1)
}
secret = 'your-secret-key'
token = jwt.encode(payload, secret, algorithm='HS256')
print(token)
# JWT検証
try:
decoded = jwt.decode(token, secret, algorithms=['HS256'])
print(decoded)
except jwt.ExpiredSignatureError:
print('Token has expired')
except jwt.InvalidTokenError:
print('Invalid token')
Java(jjwt)
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.security.Keys;
// JWT生成
String secret = "your-secret-key-must-be-at-least-256-bits";
Key key = Keys.hmacShaKeyFor(secret.getBytes());
String token = Jwts.builder()
.setSubject("1234567890")
.claim("name", "田中太郎")
.claim("email", "tanaka@example.com")
.setIssuedAt(new Date())
.setExpiration(new Date(System.currentTimeMillis() + 3600000)) // 1時間
.signWith(key, SignatureAlgorithm.HS256)
.compact();
System.out.println(token);
// JWT検証
try {
Claims claims = Jwts.parserBuilder()
.setSigningKey(key)
.build()
.parseClaimsJws(token)
.getBody();
System.out.println("Subject: " + claims.getSubject());
System.out.println("Name: " + claims.get("name"));
} catch (JwtException e) {
System.out.println("Invalid token: " + e.getMessage());
}
JWTの使用例
認証フロー
- ログイン:ユーザーが認証情報を送信
- JWT発行:サーバーがJWTを生成して返却
- リクエスト:クライアントがJWTをAuthorizationヘッダーに含めて送信
- 検証:サーバーがJWTを検証してリクエストを処理
Authorization ヘッダー
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IuOCv+ODg+OBquKAnEpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
JWTのセキュリティ
セキュリティ考慮事項
- 秘密鍵の管理:対称鍵は絶対に秘匿する
- 有効期限の設定:適切な有効期限を設定する
- HTTPS必須:JWTの送信時はHTTPSを使用
- 機密情報の除外:パスワード等の機密情報はペイロードに含めない
- 適切なアルゴリズム:セキュアな署名アルゴリズムを使用
一般的な攻撃と対策
攻撃 | 対策 |
---|---|
None アルゴリズム攻撃 | none アルゴリズムを無効化 |
鍵の混同攻撃 | アルゴリズムの厳密な検証 |
リプレイ攻撃 | 短い有効期限と nonce の使用 |
JWT の改ざん | 署名の厳密な検証 |
JWTの利点
- ステートレス:サーバーで状態を保持する必要がない
- スケーラビリティ:負荷分散が容易
- 相互運用性:異なるドメイン間でも使用可能
- モバイルフレンドリー:クッキーに依存しない
- JSON形式:JavaScript等での扱いが容易
JWTの欠点
- 取り消し困難:有効期限まで無効化できない
- サイズ:セッションIDと比較してサイズが大きい
- 機密情報:ペイロードはBase64エンコーディングのみ
- 時刻同期:サーバー間で時刻の同期が必要
JWT vs セッション
特徴 | JWT | セッション |
---|---|---|
状態管理 | ステートレス | ステートフル |
スケーラビリティ | 高い | 低い |
セキュリティ | 署名に依存 | サーバー管理 |
取り消し | 困難 | 容易 |
サイズ | 大きい | 小さい |
JWTの活用場面
- API認証:RESTful APIの認証トークン
- シングルサインオン:複数アプリケーション間でのSSO
- マイクロサービス:サービス間の認証
- モバイルアプリ:ネイティブアプリの認証
- 情報交換:異なるシステム間でのデータ交換
JWTツール
- JWT.io:JWT デコーダー・検証ツール
- jwt-cli:コマンドラインJWTツール
- Auth0 Debugger:JWT デバッグツール
関連技術
- JWS:JSON Web Signature
- JWE:JSON Web Encryption
- JWK:JSON Web Key
- OAuth 2.0:認可フレームワーク
- OpenID Connect:認証プロトコル