先确认结构
常见的 JWS 形式 JWT 由三个点号分隔的 Base64URL 段组成:
header.payload.signatureHeader 描述算法与令牌类型,Payload 放置声明,Signature 用于验证前两段未被篡改。Base64URL 只负责表示字节,因此前两段可读并不意味着签名有效。
检查 Header
{
"alg": "HS256",
"typ": "JWT"
}重点关注 alg,以及可能出现的 kid、jku、x5u 等密钥选择字段。CTF 题目可能围绕算法混淆、错误接受 none、不安全密钥选择或弱 HMAC 密钥展开,但只有在题目环境或明确授权系统中才能测试。
检查 Payload
| 声明 | 含义 | 检查点 |
|---|---|---|
| exp | 过期时间 | 通常是 Unix 秒,不是毫秒 |
| nbf | 此前不可用 | 检查服务器时钟偏差 |
| iat | 签发时间 | 用于判断令牌年龄 |
| iss | 签发者 | 服务端是否严格校验 |
| aud | 受众 | 是否被错误用于其他服务 |
| sub | 主体 | 通常是用户或实体标识 |
签名边界
本地解码工具可以安全显示 Header 和 Payload,但没有正确密钥、算法和验证规则时,不能断言 Signature 有效。把 Payload 中的 role 改为 admin 只会生成一个被篡改的令牌;安全实现应立即拒绝它。
安全判断
“JWT 解码成功”只说明格式可解析。只有服务端按预期算法、正确密钥、允许的签发者和受众完成验证后,声明才可用于授权。
CTF 分析顺序
- 复制原始令牌并保留完整三段。
- 检查段数和 Base64URL 字符。
- 读取 Header,记录算法和密钥相关字段。
- 读取 Payload,把时间戳转成 UTC 与本地时间。
- 比较题目请求、响应和服务端行为,判断验证缺口。
- 只在授权靶场中测试假设,并记录每次修改。
常见误区
- 把 JWT 当成加密容器,向 Payload 写入密码或密钥。
- 只检查
exp,忽略iss和aud。 - 根据客户端显示的角色直接做权限判断。
- 允许库根据令牌输入自动选择任意算法。