[TOC]

JWT 全称是 JSON Web Token ,是一种基于 JSON 的轻量级身份认证和信息交换规范,核心作用是在客户端和服务器之间安全传递声明(如用户身份信息)

JWT 的核心构成

JWT 由三部分组成,通过 . 连接,格式为 Header.Payload.Signature

1.Header(头部) :指定 JWT 使用的算法(如 HS256、RS256)和令牌类型。
2.Payload(载荷) :存储需要传递的声明信息(如用户 ID、过期时间),默认不加密,仅做 Base64 编码
3.Signature(签名) :用 Header 中指定的算法,结合密钥对 Header 和 Payload
进行签名,确保内容不被篡改。

作用:描述 JWT 的元数据,如签名算法和令牌类型。

内容:一个 JSON 对象,通常包含两个字段:

  1. alg:签名算法(如 HMAC SHA256、RSA 等)。

  2. typ:令牌类型(固定为 JWT)。

示例:

1
2
3
4
{
"alg":"HS256",
"typ":"JWT"
}

编码:Base64Url 编码后形成 Header 部分。

Payload

标准声明:

  • iss (issuer):签发者
  • sub (subject):主题,通常是用户ID
  • aud (audience):接收方
  • exp (expiration time):过期时间(时间戳)
  • iat (issued at):签发时间(时间戳)
  • nbf (not before):生效时间(时间戳)
  • jti (JWT ID):JWT唯一标识符

示例:

1
2
3
4
5
6
7
{
"sub": "1234567890",
"name": "John Doe",
"role": "admin",
"iat": 1516239022,
"exp": 1516242622
}

编码:Base64Url 编码后形成 Header 部分。

Signature

签名用于验证消息在传输过程中没有被篡改,对于使用私钥签名的令牌,它还可以验证JWT的发送者是否为它所声称的发送者。

**签名生成过程: **

1
signature = algorithm(  base64UrlEncode(header) + "." + base64UrlEncode(payload), secret)

JWT 算法的可逆与不可逆分类

JWT 的算法主要分为 对称加密算法非对称加密算法 ,二者的核心区别在于是否使用
“单一密钥”,这直接决定了算法的可逆性。

不可逆算法(核心用于签名验证)

这类算法仅能从 “原文” 生成 “签名”,无法从 “签名” 反推回 “原文”,核心作用是 验证数据完整性 ,而非加密。

对称加密签名算法 :使用同一个密钥(Secret Key)进行签名和验证,常见算法包括:

  • HS256(HMAC with SHA-256)
  • HS384(HMAC with SHA-384)
  • HS512(HMAC with SHA-512)

**非对称加密签名算法 ** :使用 “私钥” 签名、“公钥” 验证,私钥仅保存在服务器,公钥可公开,常见算法包括:

  • RS256(RSA with SHA-256)
  • RS384(RSA with SHA-384)
  • RS512(RSA with SHA-512)
  • ES256(ECDSA with SHA-256)
  • ES384(ECDSA with SHA-384)
  • ES512(ECDSA with SHA-512)

公钥:public key (一般会存在在响应包当中,在登陆时由客户端发送申请公钥的请求)

形式一般为:

1
2
3
————————publickey——————————————
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
—————————end key——————————————

私钥:一般存在后端服务器中,不会暴露在 web 服务器,极少数将私钥写在 js 文件中(尝试伪造)

对于 jwt 进行伪造, RS/ES 系列算法因私钥仅存服务器,即使公钥泄露,攻击者也无法伪造签名,安全性远高于 HS 系列。

对于 HS 系列的算法来说,因为仅为 单一密钥, 就可能存在风险:

  1. 密钥复用风险 :同一密钥同时负责签名生成和验证,一旦密钥泄露(如代码硬编码、配置文件泄露),攻击者可直接伪造合法 JWT。
  2. 爆破可行性 :如果密钥强度不足(如长度过短、使用弱口令、属于常见字典词),攻击者可通过以下方式爆破:
  • (1)收集合法 JWT(Header+Payload+Signature);

  • (2)使用字典或暴力枚举可能的密钥,对 Header 和 Payload 重新计算签名;

  • (3)对比计算出的签名与 JWT 中的 Signature,一致则说明密钥正确。

例如,若密钥是简单的 “123456” 或 “secret”,借助 Hashcat 等工具,几分钟内就能爆破成功。而 HS512 虽比 HS256
计算成本高,但本质上仍无法抵御强密钥的爆破(只是延长了爆破时间)。

常见攻击手法

alg 修改为 none 的无签名攻击

  • 原理: JWT 标准中定义了 alg: "none" 表示 “不使用签名”,部分实现(如早期的 jsonwebtoken 库)未严格校验这一选项,允许攻击者将 Header 中的 alg 修改为 none (大小写不敏感,如 None / NoNe / NONE ),同时删除 Signature 部分,使服务器跳过签名验证直接信任 Payload。

  • 攻击步骤:

    1. 截取合法 JWT,解码 Header,将 alg 改为 none
    2. 修改 Payload(如篡改用户角色、权限);
    3. 删除 Signature 部分(格式变为 Header.Payload. ,末尾保留 . );
    4. 提交伪造的 JWT,服务器若未校验 alg: "none" 的合法性,会直接采信 Payload。
  • **影响:**可直接篡改用户身份、权限等核心信息,无需知晓密钥。

未验证签名攻击

  • 原理: 服务器在验证 JWT 时,因代码逻辑缺陷(如未调用签名验证函数、条件判断错误),直接跳过签名校验步骤,仅解析 Payload 内容。
  • 攻击方式: 无需修改 alg ,直接篡改 Payload(如将 "role":"user" 改为 "role":"admin" ),重新 Base64 编码后提交,服务器会直接信任篡改后的内容。
  • 特点:alg: none 更直接,属于开发层面的低级错误,常见于自定义 JWT 实现中。

JWKS 公钥注入伪造攻击

  • 背景: 部分系统使用 JWKS(JSON Web Key Set,公钥集合)自动获取验证 JWT 的公钥,JWKS 通常通过 URL(如 /.well-known/jwks.json )提供。
  • 原理: 漏洞存在于 Cisco 的某些设备中,允许攻击者控制 JWKS 的 URL,注入自定义公钥,导致服务器使用攻击者提供的公钥验证签名,从而伪造任意 JWT。
  • 攻击步骤:
    1. 攻击者生成一对 RSA 密钥(私钥用于签名,公钥用于注入);
    2. 控制服务器获取 JWKS 的来源,将自定义公钥写入 JWKS;
    3. 使用私钥对篡改后的 Payload 签名,生成伪造 JWT;
    4. 服务器从恶意 JWKS 获取公钥,验证通过并信任伪造内容。

空签名攻击

  • 原理: 针对使用非对称算法(如 RS256)的 JWT,部分实现对 “空签名”(Signature 字段为空)的处理存在缺陷。攻击者可将 alg 设为非对称算法(如 RS256),同时将 Signature 设为空,服务器若错误地将空签名视为 “验证通过”,则会信任篡改后的 Payload。
  • 区别于 alg: none :此处 alg 仍为签名算法(如 RS256),但签名值为空,利用服务器对空签名的逻辑漏洞绕过验证。

算法混淆攻击

  • **原理:**利用对称算法(HS256)和非对称算法(RS256)的验证逻辑差异,欺骗服务器用错误的密钥验证签名。

  • 例如,服务器本应使用 RS256(私钥签名、公钥验证),但攻击者将 alg 改为 HS256,并使用服务器的 公钥 作为 HS256 的对称密钥对篡改后的 Payload 签名。 若服务器错误地用公钥(而非私钥)验证 HS256 签名(把公钥当对称密钥用),则会验证通过。

  • **攻击条件:**服务器同时支持 HS256 和 RS256,且公钥可被攻击者获取(如通过 JWKS 公开)。

密钥泄露导致

  • 原理: 若服务器的对称密钥(HS 系列)或私钥(RS/ES 系列)泄露(如硬编码在代码、配置文件泄露、日志泄露),攻击者可直接用泄露的密钥生成合法签名,篡改 Payload 后伪造 JWT。

  • 常见泄露场景:

    (1)开源项目代码中硬编码密钥(如 secret=123456 );

    (2)密钥存储在前端代码(如 JavaScript 文件)中;

    (3)密钥通过错误配置被写入日志文件。

(exp)篡改攻击

  • 原理: exp (过期时间)是 Payload 中的标准声明,用于指定 JWT 的有效期。若服务器未验证 exp 字段(或验证逻辑错误,如时区处理不当),攻击者可篡改 exp 为未来时间,使过期的 JWT 仍能被使用。
  • 案例: 某系统仅检查 exp 是否存在,未实际对比当前时间,攻击者将 exp 改为 9999999999 (远未来时间),使过期 JWT 永久有效。

Base64 编码漏洞

  • 原理: JWT 的 Header 和 Payload 使用 Base64URL 编码(无 Padding 或允许任意 Padding),部分实现对编码格式校验不严,攻击者可通过篡改编码后的字符串(而非解码后的 JSON),实现 Payload 篡改且签名 “看似有效”。
  • 案例: 修改 Base64URL 编码后的 Payload 字符串(如替换某个字符),使其解码后恰好为目标内容(如 "role":"admin" ),而服务器未重新编码验证,直接信任解码结果。

判断 JWT 的构造及利用方式

如果 JWT 结合了多因子校验(即验证逻辑依赖多个独立因子,而非单一 JWT 本身),那么单纯篡改 JWT 中的某一个因子(如 Payload内容、签名算法等)很可能无法绕过整体验证,攻击成功率会大幅降低。

JWT 签名 + 服务器端状态校验

  • 场景: 服务器不仅验证 JWT 的签名和内容,还会查询后端存储(如 Redis、数据库)中该 JWT 的状态(如是否被吊销、是否与用户当前会话匹配)。
  • 攻击限制: 即使攻击者通过 alg: none 或空签名篡改了 JWT 的 Payload(如提升权限),服务器查询后端状态时会发现 “该 JWT 的权限信息与存储的原始记录不符”,从而拒绝请求。 例如:用户登录时生成的 JWT 对应 role:user ,服务器在 Redis 中记录了该 JWT 的唯一 ID 及其真实权限,篡改 JWT 的 role admin 后,服务器对比 Redis 记录会发现不一致,直接拦截。

JWT 签名 + 短期有效期(exp) + 动态 Token or uuid 绑定

  • 场景: JWT 的 exp 设置为极短时间(如 1 分钟),同时客户端需定期用 “刷新 Token”(独立于 JWT,且存储在服务器)获取新 JWT,且刷新 Token 与用户 IP / 设备绑定。使用 uuid 则因为 uuid 极其难以遍历。
  • 攻击限制: 即使攻击者篡改了 JWT 的 exp 延长有效期,或伪造了签名,由于 JWT 本身很快过期,且刷新 Token 与攻击者的 IP / 设备不匹配,无法获取新的有效 JWT,攻击窗口极短。

JWT + 附加校验参数(如客户端指纹、请求上下文)

  • **场景:**JWT 的 Payload 中包含客户端指纹(如浏览器 UA、设备 ID 的哈希值),服务器验证时不仅检查 JWT 签名,还会对比当前请求的客户端指纹与 Payload 中的记录是否一致。