news 2026/10/21 6:18:31

JWT靶场实战:弱密钥爆破、算法混淆与签名校验缺失全套路解析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
JWT靶场实战:弱密钥爆破、算法混淆与签名校验缺失全套路解析

JWT 这东西,平时在业务系统里就是个“门禁卡”,后端一验签、一解包,就知道你是谁、能进哪个房间。可在靶场里,它就是一套出题人精心布置的机关,表面看三段字符串长得差不多,实际坑位一个比一个深。我近期把某套 JWT 综合靶场从头到尾刷了一遍,从最基础的弱密钥爆破,到明显偏脑筋急转弯的算法混淆,再到平时很少有人注意的“签名校验缺失”,算是把这类题目的套路摸了个七七八八。这篇文章就按我当时解题的顺序来写,把每一类题型的判断方法、实操步骤和容易踩的坑都整理出来,适合正在刷 Web 安全题、准备攻防演练,或者单纯想搞清楚 JWT 到底哪里脆弱的同学参考。

1. 为什么 JWT 会成为靶场出题重灾区

1.1 JWT 在真实系统里的普适性

先说一下我为什么觉得 JWT 是靶场绕不开的考点。现在稍微新一点的 Web 系统,登录之后返回一个 JWT 作为会话凭证,这个模式基本是标配。前端把 token 放在本地存储里,每次请求带上,后端验一下签名就放行,既不需要服务端存 session,又天然“无状态”,横向扩容也方便。正因为它应用太广,安全问题一旦出现,影响面就非常大。

靶场出题人自然也会盯着这类“高频且高价值”的技术点。一个 JWT 题目做得好,能把编码、签名、密钥管理、服务端校验逻辑全串起来,考察面非常广,而且从静态代码分析到动态请求测试都能覆盖。对刷题的人来说,做一道 JWT 题,产出的东西也远远不止一个 flag,而是对认证体系整体安全性的理解。

1.2 靶场出题人的常见思路

我把刷过的题目复盘了一下,发现出题人的思路基本围绕几个维度展开:

  • 密钥相关的:密钥弱、密钥泄露、密钥可预测。
  • 算法相关的:算法混淆、不安全的算法声明、none 算法。
  • 校验相关的:服务端压根没验签、只解包不验签、验签了但异常处理不正确。
  • 业务逻辑相关的:修改用户身份、伪造管理员角色、篡改过期时间、越权访问。

这四类里面,“校验相关”和“业务逻辑相关”往往是同一道题里同时出现的。比如你伪造了一个管理员 token,但后端根本不验签,那这道题既考了你对 token 结构的理解,也考了服务端实现缺陷的利用。理解出题人的套路之后,刷题效率就会高很多,看到一个 token 你能第一时间判断该往哪个方向试。

2. JWT 三段式结构:还没改 Payload 之前先理解签名是怎么出来的

2.1 Header、Payload、Signature 各管什么

JWT 长什么样呢?就是三个用点号分隔的字符串,整体类似这样:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImFkbWluIiwicm9sZSI6ImFkbWluIn0.sQZuJhPmo28Vg3ZSsHhJmDkX9bE5cDcl9T8DNjkvkyA

第一部分是 Header,一般藏着算法声明;第二部分是 Payload,承载具体的业务声明;第三部分是 Signature,由“头部 + 载荷 + 密钥”按约定算法算出来。

Header 最典型的例子是:

{"alg":"HS256","typ":"JWT"}

这里的 alg 声明了签名算法,typ 基本可以忽略,大部分实现里就是纯标识。Payload 里常见的字段有 username、role、user_id、exp、iat、iss 等,这些字段往往就是题目里要改的目标。

2.2 签名算法分类:HS 系列和 RS/ES 系列

签名算法大致分成两类:

  • 对称算法 HS256/HS384/HS512:加密和解密用的是同一个密钥。服务端拿到 token,用自己保存的密钥重新算一遍签名,跟 token 里带的签名比对,一样就通过。问题是这个密钥一旦泄露,或者选得太简单,攻击者就能自己伪造 token。
  • 非对称算法 RS256/ES256 等:签名用私钥,验签用公钥。服务端只拿公钥验签,私钥保管在更安全的地方。理论上就算公钥公开,攻击者也没法伪造签名,因为只有私钥能签名。

理解这一层区别非常重要,因为后面说的“算法混淆攻击”就是故意让服务端把“非对称验签”误当成“对称验签”来用。

2.3 签名生成过程的底层逻辑

以 HS256 为例,签名的计算过程可以写成:

signature = HMAC_SHA256(secret, base64url(header) + "." + base64url(payload))

也就是说,把 Header 和 Payload 分别做 base64url 编码,中间用点号拼起来,再用密钥做一次 HMAC-SHA256 运算,得到的结果再做 base64url 编码,就是签名。

RS256 就复杂一些,它是用 RSA 私钥对整个“header.payload”字符串做签名,验签的时候用公钥。这个过程的重点在于:整个签名校验的核心输入是“header 里写的算法”和“服务端保存的密钥”。

很多新手不理解,为什么改了 payload 后面签名就失效了。原因很简单,签名是拿原始 payload 算出来的,只要 payload 动一个字节,重新计算的签名就完全不同。除非你已经拿到了密钥,或者服务端压根不校验签名,否则盲改 payload 基本没用。

2.4 base64url 编码的常见细节

JWT 用的编码不是普通 base64,而是 base64url。区别在于普通 base64 里会有+、/、=这几个字符,而 base64url 把+换成-,把/换成_,结尾的=通常也会去掉。

我见过不少人在改 payload 的时候,直接拿在线 base64 工具编码完就拼进 token 里,结果发现服务端报错或者验签不过。原因就是编码方式不一致。你在手动改 token 的时候,一定要确认用的是 base64url 且不带填充符号,否则很容易在题目里绕圈子浪费时间。

3. 高频题型拆解:从弱密钥爆破到算法混淆

3.1 弱密钥爆破:拿到 token 先试这一招

刷 JWT 题时,看到 HS256 的 token,第一反应就是猜密钥。很多靶场为了防止题目太难,会把密钥设成常见弱口令,比如secret、password、123456、admin之类。也有的会把密钥放在字典里等着你爆破。

我的判断顺序是:

  • 先看 header 里 alg 是不是 HS256,如果是,说明是共用密钥体系,爆破可行性高。
  • 再用常见弱口令字典直接试。
  • 如果常见字典没跑出来,再考虑从题目环境里找线索,比如网页源码、JS 文件、注释、接口返回等地方可能藏着密钥。

爆破工具方面,hashcat 可以直接解 JWT 的 HS256 模式:

hashcat -m 16500 jwt.txt wordlist.txt

jwt.txt 里放完整的 JWT 字符串,wordlist.txt 放字典。跑出来之后,就可以用得到的密钥伪造任意身份了。

这里说个坑:有些靶场用的密钥是随机长字符串,那基本不是靠爆破能解决的,你就得换个思路,比如去找服务端配置文件、Git 仓库泄露、备份文件之类的信息泄露点。

3.2 算法混淆:RS256 怎么被 HS256 带偏的

算法混淆是我刷这套题时觉得最绕的一种题型,也是面试和攻防里很喜欢考的点。

它的核心逻辑是这样的:服务端本来用的 RS256 验签,也就是用公钥验证签名,结果攻击者把 Header 里的算法改成 HS256,同时用公钥作为 HMAC 的密钥去生成新签名。如果服务端支持根据 Header 动态选择算法,并且没有限制算法白名单,那么服务端就会用公钥去当 HS256 的密钥来验签——这时候攻击者已经用这个“公钥”签了一个 token,验签自然就通过了。

这个攻击成立的两个前提是:

  • 服务端验签时信任 Header 里的 alg 字段。
  • 攻击者能拿到公钥。公钥通常藏在靶场某个接口里,或者以.pem文件、JWK 格式直接暴露。

实操上,你可以先找公钥,然后构造一个 Payload,把 alg 改成 HS256,再用公钥字符串作为 HMAC 密钥生成签名。最终得到的 token 放进请求里,如果服务端不再报签名错误,说明算法混淆攻击成功了。

注意,公钥作为 HMAC 密钥时,具体用的字符串格式要和服务端代码一致。有的场景直接用-----BEGIN PUBLIC KEY-----整段,有的场景用的是去掉头和换行后的 base64 字符串,这个细节我在后面的排坑部分会专门说。

3.3 签名校验缺失:最“朴实”但最实用的一类题

签名校验缺失听起来很低级,但在实战里并不少见。比如有些开发者在写代码时,只解析了 token 的 Payload,根本没验证签名,或者已经验证签名但在异常分支里直接返回了 Payload 而非报错。

碰到这种题,解题步骤极其简单:

  • 把 JWT 复制下来。
  • 解码 Payload 部分。
  • 把 username、role、user_id 改掉。
  • 重新 base64url 编码,拼回去,签名部分原封不动甚至随便填一串字符。

这时候你可能会想:“签名都不对,服务端怎么会认?”可现实就是,服务端根本没验签,它只解 Payload,自然认不出你已经动过手脚了。

判断这类题的方法也很简单:拿一个明显篡改过的 token 去请求,如果服务端没有报签名相关错误,而是直接正常响应或者只报“用户不存在”这类业务错误,那大概率就是没验签。

3.4 密钥泄露与硬编码:题面线索往往就在眼前

还有一类题,密钥并不是靠爆破解出来的,而是直接藏在你能看到的地方。

常见藏法:

  • 网页源码里的 JS 注释。
  • 接口返回里的调试信息。
  • 静态资源文件里的配置备份。
  • 响应头里的自定义字段。
  • 前端打包文件里的环境变量。

刷题的时候千万不要一头扎进去爆破。先把靶场提供的所有页面、接口、静态文件过一遍,看看有没有和 key、secret、token 相关的字眼。就拿我之前刷的一道题来说,密钥直接写在登录接口的响应注释里,开发者加了个// key=testkey123之类的调试信息,后面被带到生产环境了。这种题考察的就是信息收集能力和细心程度。

拿到密钥之后,无论 HS256 还是 RS256,你都能直接构造任意 token,剩下的事情就是伪造管理员身份然后取 flag。

3.5 none 算法与过期时间绕过:理解服务端异常处理

none 算法这种题比较老,现在很多 JWT 库已经默认禁止了,但靶场里为了教学还是会保留。原理是 Header 里写入{"alg":"none","typ":"JWT"},签名部分留空,如果服务端解析时没有对“空签名”做校验,就能直接通过。

处理办法是:

  • 把 Header 改为 none。
  • Payload 改成想要的身份。
  • 签名部分干脆不写,变成header.payload.或者header.payload(视服务端实现而定)。

过期时间绕过也属于这一类。有的靶场给你一个 token 但已经过期,如果你想继续用,就先把 exp 改成未来的时间戳,再重新签名。如果服务端没有认真校验 exp,或者说异常处理不严谨,依然会通过。还有一种情况是 exp 字段缺失,服务端可能直接当成永不过期处理。

4. 实操落地:解码、爆破、伪造、验证一条龙

4.1 准备一个趁手的实验环境

刷题前我习惯先把工具备齐。本地装好 Python 3 环境,装一个 PyJWT 库,再准备一个常见的 JWT 字典和 hashcat(如果要用)。不装也行,但后面有些自动化的伪造和批量测试还是会用到。

安装 PyJWT 的命令很简单:

pip install pyjwt

版本不同,API 有区别,后面我会提到新老版本的差异。还有一个非常好用的命令行工具叫 jwt_tool,它把解码、爆破、算法混淆、none 攻击这些都集成在一起了,适合快速验证思路。不过我建议你先把原理弄清楚再依赖工具,不然工具跑不出来你都不知道问题出在哪。

4.2 解码分析:先看结构再定策略

拿到一个 JWT,第一步永远是“拆开看”。用 PyJWT 可以直接解码但不验签:

import jwt token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImFkbWluIiwicm9sZSI6ImFkbWluIn0.sQZuJhPmo28Vg3ZSsHhJmDkX9bE5cDcl9T8DNjkvkyA" data = jwt.decode(token, options={"verify_signature": False}) print(data)

这样不需要密钥就能看到 Payload 里的内容。你的任务就是根据这些内容判断要改什么:是改 username 还是改 role,还是把 user_id 改成某个已知的管理员 ID。

同时看 Header 里的 alg,如果是 HS256,准备爆破;如果是 RS256,先找公钥;如果完全没有签名,进入无验签路线。

4.3 弱密钥爆破与伪造示例

假设你已经判断目标是 HS256,而且密钥就是字典里的某个词。先用 hashcat 爆破,得到密钥testkey123,下一步就是直接伪造一个管理员 token:

import jwt payload = { "username": "admin", "role": "admin", "user_id": 1 } key = "testkey123" token = jwt.encode(payload, key, algorithm="HS256") print(token)

这里要注意,旧版 PyJWT 的jwt.encode(payload, key, algorithm="HS256")能直接用,新版则要求传入algorithms列表或者使用algorithm参数,写法大体兼容。生成之后,拿到靶场接口里替换原来的 token 进行测试。

4.4 算法混淆的模拟过程

RS256 转 HS256 的题,我一般先把公钥保存成public_key.pem,然后用 Python 脚本伪造 token:

import jwt public_key = open("public_key.pem", "r").read() payload = { "username": "admin", "role": "admin" } token = jwt.encode(payload, public_key, algorithm="HS256") print(token)

这段代码的本质是把公钥内容当作 HS256 的对称密钥来用。如果服务端没有正确配置算法白名单,就会用同一个公钥去验签,结果自然能通过。这里有个细节,有些靶场的公钥是直接给的一段字符串,而不是 PEM 格式文件,你需要手动拼成统一格式再试。

4.5 把伪造好的 token 放到请求里

伪造只是第一步,真正出结果要看服务端认不认。请求时通常用 Authorization 头:

Authorization: Bearer <你的token>

也有的靶场把 token 放在 Cookie 里,字段名可能是 token、jwt、session 之类的,你得先看登录接口的返回和请求包。用 Burp Suite 或者浏览器开发者工具改一下请求,替换成新 token,再访问需要管理员权限的接口。返回 200 并且拿到 flag,这道题就算通关了。

4.6 通用自动化脚本的思路

刷的题多了之后,你会发现很多步骤是重复的,于是我写了一个很小的工具函数,方便快速测试不同思路:

import jwt def create_token(payload, key, algorithm): if algorithm == "none": header = {"alg": "none", "typ": "JWT"} encoded_header = jwt.utils.base64url_encode(jwt.utils.bytes_to_string(jwt.utils.json.dumps(header).encode())) # 这里只演示思路,none 场景通常需要手工拼字符串 return None return jwt.encode(payload, key, algorithm=algorithm)

当然,这只是个框架,具体使用时还要根据字典爆破的结果和目标服务端的响应来做调整。写脚本的目的不是炫技,而是把反复操作的部分收拢起来,减少人工失误。

5. 排坑手册:我刷 JWT 题时踩过的雷

5.1 改了 Payload 却没重算签名

这是新手最常见的错误。很多人解码 Payload 后,直接把修改后的内容 base64 一下拼回去,忘了签名是拿原 Payload 算的。服务端一验签,立刻就发现签名不匹配。正确做法是:在没有密钥的情况下就只考虑“无验签”型题目;如果判断需要验签通过,就必须先用密钥或公钥重新算签名。

5.2 公钥格式不统一导致伪造失败

我在做算法混淆题的时候,被公钥格式坑过一次。从接口拿到的公钥是-----BEGIN PUBLIC KEY-----开头、-----END PUBLIC KEY-----结尾的完整 PEM 文件,但靶场代码里使用的是去掉换行的紧凑格式。我直接用完整 PEM 去签名,结果服务端验签一直失败,后来才发现它内部把换行去掉了。

建议多准备几个格式版本:

  • 完整 PEM。
  • 去掉头和尾的 base64。
  • 去掉所有换行后的紧凑串。

逐个试一遍,总能撞对。

5.3 看错了服务端的校验逻辑

有些题目表面上是 JWT 题,但核心考点并不在签名。比如服务端虽然验签,但它验签之后还会拿着 Payload 里的 username 去数据库查一次真实角色,这时候你光把 role 改成 admin 是没用的,得把 username 也改成数据库里真实管理员的名字。

遇到这种情况,我的经验是先看接口响应。如果伪造 token 后报“用户不存在”之类的错误,说明服务端拿 Payload 里的身份去查库了;如果报“权限不足”,说明身份校验和角色校验是分开的,可能还藏着一个查询或者接口逻辑要处理。

5.4 exp 时间戳的单位换算

JWT 里的过期时间 exp 是 Unix 时间戳,单位是秒。有一次我为了绕过过期时间限制,直接把当前时间戳不乘 1000 填进去,结果服务端判断 token 已经过期,反复报错。后来才发现自己把它当成了毫秒。

记住一个规律:

  • 如果当前时间是1700000000十位数,直接当秒填。
  • 如果当前时间是1700000000000十三位数,那是毫秒,除非服务端特殊处理,否则要除以 1000 再填。

5.5 PyJWT 新老版本接口差异

以前写jwt.decode(token, verify=False)还能用,新版本已经强制要求options={"verify_signature": False}这种写法。伪造 token 时,新版本要求明确指定 algorithm 参数,不填会直接报错。建议刷题前固定一个虚拟环境,省得被版本差异干扰。

5.6 别忽略“签名校验失败”和“解析失败”的语义区别

服务端返回错误时,你要区分是签名校验失败还是 JWT 结构解析失败。前者说明服务端确实验签了,如果攻击方向对,那就是密钥或者算法问题;后者说明服务端可能连格式都不认,或者你拼接的字符串有问题。不要看到 401 就以为是验签失败,先详细看一下错误信息。

6. 解完题之后:真实业务里如何补上这些漏洞

6.1 算法层面:白名单控制

靶场里刷到的所有 JWT 漏洞,映射到真实系统里,第一道防线就是算法白名单。服务端在验证 token 时,不能盲目信任 Header 里的 alg 字段,要固定允许的算法列表。比如后端只允许 RS256,那遇到 Header 里写着 HS256 的 token,直接拒绝,而不是真的去用公钥做 HMAC 验签。

很多成熟 JWT 库已经默认实现了算法白名单,但如果代码里写成“动态获取 alg”再传给库函数,绕过白名单的问题就又出现了。代码评审里这一点特别值得关注。

6.2 密钥管理:弱密钥是万恶之源

我相信刷完 JWT 题的同学,对“密钥不能乱设”这件事都会印象极深。一个 HS256 token,密钥设成secret,就等于把门禁卡制作权限交给了所有人。生产环境一定要用足够长度的随机密钥,并且定期轮换。密钥轮换机制要考虑旧 token 的兼容性,但不能因为兼容就永久保留旧密钥。

6.3 校验逻辑:先验签再解析

服务端的校验顺序应该是“先完整验签,再解析 Payload,再做业务判断”。很多漏洞都是因为先解析了 Payload,或者解析失败时错误地走到了默认放行分支。这里有个反模式很常见:开发者在异常捕获里返回了 Payload 而不是错误,导致签名校验成了摆设。

建议写一个统一的认证中间件,负责解析和验签,所有业务接口都只从这个中间件的上下文里读取用户身份,而不是各自去解析 token。

6.4 日志与监控:看到异常请求能溯源

真实攻防里,攻击者伪造 token 的行为不会每次都成功,失败尝试也会留下痕迹。服务端应该记录 JWT 验签失败、alg 异常、token 过期等关键事件。我个人的经验是,把“验签失败”单独记录成一个告警项,比记录所有请求靠谱得多。因为正常业务里,验签失败的比例非常低,一旦突然升高,大概率有人在打你的认证接口。

6.5 失效与续期:别让 token 一劳永逸

最后多说一句,JWT 是无状态 token,签发之后服务端很难主动让它立刻失效。业务上如果涉及封号、踢人、权限变更,单纯靠 JWT 的过期时间往往不够实时。可以考虑引入 token 版本号、用户状态校验或者把关键权限从 token 里拿出来再查库。这一点靶场题里遇不到,但真实系统必须面对。

刷完这套 JWT 靶场,我最大的体会就是:JWT 本身并不复杂,复杂的永远是使用它的上下文。无论是爆破、算法混淆还是无验签利用,都是在和服务端代码的“懒惰”和“信任”博弈。把这些题做明白,再回头看自己负责的系统,你会不自觉地想检查一下算法白名单、密钥长度和异常处理分支。这种“解题之后的后怕”,大概就是刷靶场最值钱的收获。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/21 6:17:04

Kubernetes核心对象实战:从Pod探针到Ceph持久化存储

在Kubernetes里摸爬滚打久了&#xff0c;你会意识到所谓的K8s资源对象并不是一堆抽象概念&#xff0c;而是日常排障、架构设计、容量规划时真正要反复打交道的东西。这篇文章我把API、Pod探针、Service、Volume、StatefulSet、DaemonSet、PV/PVC这些核心对象串起来&#xff0c;…

作者头像 李华
网站建设 2026/10/21 6:16:45

Delphi原生屏幕找图:灰度化与模板匹配实战

简介&#xff1a;这份Delphi图像匹配资源面向需要在Windows桌面环境中实现屏幕找图的开发者&#xff0c;适用于自动化测试、游戏辅助、屏幕抓取等场景&#xff0c;属于图像处理与计算机视觉的入门到进阶实践内容。资源包为rar压缩格式&#xff0c;整体约6.63MB&#xff0c;上游…

作者头像 李华
网站建设 2026/10/21 6:16:41

社交媒体用户关系网络分析:Python爬虫与图数据挖掘实战

做社交网络分析这几年&#xff0c;我最常被问到的问题就是&#xff1a;那些大V到底是怎么影响普通用户的&#xff1f;一个话题是怎么从某个小圈子火起来的&#xff1f;团队内部谁是真正的信息枢纽&#xff1f;说实话&#xff0c;这些问题用传统的关系型数据库和统计报表很难回答…

作者头像 李华
网站建设 2026/10/21 6:16:03

助农管理系统毕设实战:从Spring Boot后端到Vue前端全链路拆解

选毕设题目那会儿&#xff0c;我见过太多人一头扎进“XX管理系统”的模板里出不来。但“助农管理系统”这个选题&#xff0c;跟普通的图书管理、学生管理比起来&#xff0c;有个非常讨巧的优势&#xff1a;它自带一个完整的业务闭环&#xff0c;有账号体系、商品流转、订单状态…

作者头像 李华
网站建设 2026/10/21 6:15:46

Delphi原生屏幕找图实战:截屏、模板匹配与坐标返回

简介&#xff1a;这份资源面向使用Delphi进行屏幕图像匹配的开发者&#xff0c;聚焦自动化测试、游戏辅助、屏幕抓取等场景&#xff0c;帮助解决在屏幕中快速定位目标图片的问题。内容围绕图像处理库选型、屏幕抓取、模板匹配、哈希与特征匹配算法、效率优化及误报漏报处理展开…

作者头像 李华
网站建设 2026/10/21 6:15:22

教培机构销售工作台:公海与私海客户流转机制设计

1. 公海与私海&#xff1a;先搞清楚客户池怎么分干教培销售管理这些年&#xff0c;我见过太多团队死磕“线索分配不均”这个问题。新顾问抱怨拿到的名单质量差&#xff0c;老顾问嫌手里的客户跟不动&#xff0c;主管夹在中间天天当裁判。其实问题往往不在人&#xff0c;而在客户…

作者头像 李华