jwt-weakness

star 72

JWT 弱密钥与信息泄露检测 — 检测 JWT 算法配置、密钥强度与敏感 claims 暴露;适用于登录认证、API 网关与单点登录场景。

Q16G By Q16G schedule Updated 6/7/2026

name: jwt-weakness description: JWT 弱密钥与信息泄露检测 — 检测 JWT 算法配置、密钥强度与敏感 claims 暴露;适用于登录认证、API 网关与单点登录场景。 when-to-use: 当任务涉及鉴权令牌安全、JWT 签名校验与 token 泄露风险评估时 allowed-tools: bash,read_file,list_files,rg user-invocable: false

JWT 弱密钥与信息泄露检测

成因引用

JWT Token / 凭证字符串(用户可控的签名与 claim 载荷)→ JWT 验证逻辑(服务端 HMAC 密钥校验 / 算法决策 / claim 信任 / 过期判定)= JWT 弱密钥与信任滥用类漏洞。一句话讲清成因:服务端把"客户端递交的 token 当作可信凭证",签名校验、算法选择与 claim 信任任一环失守,攻击者就能伪造或重放越权 token。业务命名不可作筛选——不能用"这是登录接口/这是网关 token/这个字段叫 access_token"作为是否检测的依据,是否存在 JWT 弱点要按 sink 语义(签名校验代码路径、算法白名单逻辑、claim 信任面、过期判定)判断;同一系统里 SSO 网关 token、刷新 token、设备 token 完全可能采用不同密钥与不同算法。详见同根目录 pentest/web-security-testing/SKILL.md 漏洞成因图谱 · JWT 弱密钥与信任滥用行(不在本 skill 重复成因)。

触发线索(基线检查项)

以下是已知的常见 JWT 弱密钥与信息泄露触发线索,作为基线起点而非必检硬清单:

  • 适用且已完成 → 标注 [x] done
  • 明确不适用 → 标注 [-] n/a (原因)
  • 基线未列出但实际发现 → 新增条目并标注 [+] added (来源)

基线触发线索按"sink 语义"分类(不按业务命名):

  • 签名校验 sink:响应或请求头中出现 eyJ 开头的三段式字符串;Authorization: Bearer <jwt>;Cookie 名为 token / access_token / id_token / session 且值为 JWT 形态;前端 localStorage / sessionStorage 写入 JWT。
  • 算法决策 sinkalg=HS256/HS384/HS512 且无密钥强度声明;alg=none 仍被接受;同时存在 RS256 公钥可读端点(.well-known/jwks.json/oauth/keys)且服务端未锁定算法白名单。
  • claim 信任 sink:payload 含 role / isAdmin / scope / tenant_id / user_id 等权限判定字段;payload 含明文敏感数据(手机号、邮箱、身份证、内部 ID、API key 片段);iss / aud 字段未在服务端校验。
  • 过期与撤销 sinkexp / nbf / iat 字段存在但服务端是否拒收过期 token 未知;登出后旧 token 是否仍被接受未知;无 jti / 黑名单机制。
  • 代码模式:后端代码出现 jwt.parse(token)jwt.decode(token, verify=False)new JwtParser().setSigningKey(...).parseClaimsJws(...) 等"签名校验或解码"形态;密钥来源为常量字符串、配置文件明文、环境变量但默认值为弱口令。

思考检查点

加载本 skill 时按这些问题思考:

  • 这个 token 的签名校验代码路径在哪?密钥从何处加载?算法是否硬编码还是从 token 头部读取?
  • 服务端是否同时接受多种算法(HS / RS / none)?算法白名单是收敛到一种还是只看 token 自己声明的 alg
  • 哪些 claim 被用来做权限判定?这些 claim 在服务端是否被独立校验,还是直接信任 token 内容?
  • 弱密钥结论是否可继续推到"伪造 token 能访问受保护资源"?只有签名能伪造不算闭环。
  • 跨子系统(网关 token / SSO / 内部服务间 token)是否各自有独立密钥?任一子系统弱密钥都不能推断其他子系统也弱或强。

前置条件与安全边界

  • 仅分析授权获取的 token,禁止采集或传播无授权敏感凭证。
  • 弱密钥验证仅限小规模受控字典(首选项目内置 jwt.secrets.txt 或同等规模),不进行高强度暴力尝试。
  • 非 HMAC 算法(如 RS256/ES256)不做"弱密钥爆破"结论;仅检查算法混淆、claim 信任与过期判定面。
  • 命中高风险后立即停止扩展尝试并进入修复建议阶段,不在生产环境继续构造越权请求。

检测步骤

Step 1:基线采集

提取请求中的 Authorization header / Cookie / body / 前端存储中的 JWT;记录采集位置(哪个端点的请求/响应)以便后续端点矩阵传播登记。

Step 2:解码分析

调用 jwt_decode 做无签名校验解码,记录:

  • 算法 alg、密钥 ID kid
  • 过期相关字段 exp / nbf / iat
  • 身份与权限相关 claims(sub / role / scope / tenant_id / isAdmin / iss / aud
  • payload 中是否含敏感明文(手机号、邮箱、内部 ID、API key 片段)

Step 3:弱密钥验证(条件执行)

若为 HMAC 算法(HS256/HS384/HS512):

  • 已知或可猜测密钥时优先尝试定向 key 解密;
  • 未知时调用 jwt_crack 并按需开启内置字典批量验证(use_builtin_wordlist=true)。

非 HMAC 算法(RS256/ES256):

  • 不做弱密钥爆破,转而验证"算法混淆"——尝试把 alg=RS256 改成 alg=HS256,把公钥当 HMAC 密钥使用是否被服务端接受。
  • 验证 alg=none 是否被接受(移除签名段)。

Step 4:闭环利用验证

将命中的密钥或绕过形态用于伪造一个新 token(最小化篡改,如把 role=user 改为 role=admin),重发到受保护端点;观察服务端是否真的据此放权。仅能解码或能签出新 token 但服务端未接受,不能判 confirmed。

示例库

正例形态(代码层根因)

  • jwt.parse(token).getBody()(无算法白名单约束)— 服务端未限制算法,攻击者可改 alg=none 绕过签名(jwt-alg-none-accepted
  • new JwtParser().setSigningKey(publicKeyPem).parseClaimsJws(token)(同时支持 RS256 与 HS256)— 经典 RS256→HS256 算法混淆(jwt-rs256-to-hs256-confusion
  • HMAC 密钥来自 application.ymljwt.secret: secret123 — 硬编码弱密钥(jwt-hs256-weak-secret-bruteforce
  • claims.get("role") 直接用于权限判定且无服务端二次校验 — claim 篡改无防护(jwt-claim-tampering-no-verify

窄化反例(必须避免)

以下是 JWT 弱密钥与信息泄露维度的典型窄化误判:

  • "已测 HS256 弱密钥就跳过 alg=none / 算法混淆" — 错。HS256 弱密钥、alg=none、RS256→HS256 混淆是三个独立的 sink 语义形态,密钥强度判定不能替代算法决策面的覆盖。
  • "JWT 看起来挺长(300+ 字符)就跳过弱密钥" — 错。token 长度由 payload 决定,与密钥强度无关;密钥强度按 Shannon 熵或字典命中判定,不能用 token 字面长度推断。
  • "某子系统的 JWT 是强密钥就推断其他子系统也是" — 错。网关 token、刷新 token、设备 token、内部服务间 token 通常由不同模块签发,必须跨子系统独立结账。
  • "Token 含 exp 字段 → 已防过期" — 错。exp 是 claim 声明,需要服务端实际验证;很多实现只解码不校验时间,必须用过期 token 重放确认服务端是否拒收。
  • "参数名/字段名不含 token / jwt 就跳过" — 错。按 sink 语义(任何三段式 Base64URL 形态、任何被服务端当作签名凭证使用的字符串)筛选,不按命名筛选。

反例义务(必须遵守)

为什么这里是「必须」:反例义务属于交付契约——"该子系统无 JWT 弱点"或"已防护"结论是覆盖完整性的产物声明,缺失反向验证清单会让下游误信"该维度全站安全"。

写"未发现 JWT 弱点"或"已防护"前,产物必须包含:

  • 测过的 JWT 候选端点完整清单(按 sink 语义枚举,按参数名筛选;含 Authorization 头、Cookie、localStorage/sessionStorage、内部服务间调用、SSO 回调、刷新接口)
  • 每个端点测过的形态:弱密钥字典命中、alg=none、RS256↔HS256 混淆、claim 篡改、过期重放
  • 每个端点的响应证据(HTTP 状态、响应体关键字段、是否放权)

清单不完整 → 结论降级为 partial-coverage 并显式声明未覆盖范围。

特别警示:最容易漏的是"算法决策面跨签发模块差异"——主登录走 RS256 强密钥,刷新 token 走 HS256 弱密钥;只看一处会直接判全站安全。每个独立签发模块都要单独跑一遍弱密钥与算法混淆。

闭环验证要求(必须遵守)

通用闭环口径见同根目录 common/closure-verification.md(技能表 path 列同一抽取根下,需要时 read_file 读取)。核心:完整证据链才判 confirmed,中间信号最多 suspected。本漏洞特有要点:

  • jwt_crack 命中弱密钥、仅能离线伪造 token——属中间信号,需进一步把伪造 token 发到受保护端点并观察服务端放权才能 confirmed
  • claims 泄露需证明接收方范围不应看到这些内容(如普通用户的 token 含他人手机号),仅"payload 含敏感字段"无法判 confirmed。
  • 算法混淆与 alg=none 必须用伪造后的 token 实际请求受保护资源,仅能成功签名不算闭环。

判定标准

现象 判定
jwt_crack 明确命中 HMAC 签名密钥,且据此最小化伪造/篡改 token 后被服务端接受并放权 confirmed
alg=none / 算法混淆 / claim 篡改后的 token 被服务端接受并放权 confirmed
命中弱密钥但未验证受保护资源接受;或 claims 暴露不必要敏感信息但影响范围未确认 suspected
算法与内容均无明显问题,且未出现弱密钥命中证据 not vulnerable
覆盖清单不完整(如未跨签发模块测算法混淆/未测 alg=none) partial-coverage

修复建议

  • HMAC 模式使用高强度随机密钥(≥ 256 bit,Shannon 熵 ≥ 7.0)并定期轮换,避免默认/弱口令密钥。
  • 优先采用非对称签名(RS256/ES256)并严格管理私钥;服务端锁定单一算法白名单,禁止从 token 头读取 alg
  • 显式拒绝 alg=none;接收端始终用对应算法的密钥校验,杜绝 RS256/HS256 混淆。
  • claims 仅保留最小必要信息,避免放入敏感身份与业务数据;权限判定在服务端独立查库二次校验。
  • 严格校验 exp/nbf/iss/aud,服务端实现 token 撤销(黑名单 / 短 TTL + 刷新机制)。
Install via CLI
npx skills add https://github.com/Q16G/aster --skill jwt-weakness
Repository Details
star Stars 72
call_split Forks 6
navigation Branch main
article Path SKILL.md
More from Creator