🎫
← 返回教程列表

如何验证 JWT 签名:HS256、RS256 和 ES256

· 标签: jwt-verification, jwt-signature, hs256, rs256, token-validation

如何验证 JWT 签名

JWT 以明文形式携带其声明。防止伪造的唯一手段就是签名。如果没有验证,攻击者可以构造任意令牌并冒充任何用户。本指南介绍三种最常见的 JWT 签名算法以及如何逐一验证。

验证流程

无论采用哪种算法,JWT 签名验证都遵循相同的步骤:

  1. . 分割令牌,提取 header、payload 和 signature。
  2. 对 header 进行 base64url 解码,读取 alg 字段。
  3. 使用算法和密钥,根据 header 和 payload 重新计算签名。
  4. 将重新计算的签名与令牌的签名进行比较。
  5. 如果二者匹配,则令牌是真实的。

HS256(HMAC-SHA256)

HS256 是一种对称算法——同一个密钥既用于创建签名,也用于验证签名。

signature = HMACSHA256(
  base64urlEncode(header) + "." + base64urlEncode(payload),
  secret
)

要验证,使用共享密钥重新计算 HMAC 并进行比较:

Token:   header.payload.sig
Secret:  "my-shared-secret"

1. Compute: HMACSHA256("header.payload", "my-shared-secret")
2. Compare: computedSignature === sig

安全注意事项: 密钥必须保密、定期轮换,并且至少具有 256 位的熵。对于许多服务都需要共享密钥的微服务架构来说,HS256 并非理想选择。

RS256(RSA-SHA256)

RS256 是非对称的。签发方使用私钥签名;验证方使用对应的公钥进行验证。

signature = RSASHA256(
  base64urlEncode(header) + "." + base64urlEncode(payload),
  privateKey
)

要验证,使用公钥:

Token:   header.payload.sig
Public:  "-----BEGIN PUBLIC KEY-----..."

1. Load the RSA public key
2. Verify: RSA_SHA256_verify("header.payload", sig, publicKey)

优点: 签名密钥和验证密钥是分开的。任何服务都可以验证而无需持有密钥。公钥可以通过 JWKS 端点提供,便于轻松轮换。

ES256(ECDSA-SHA256)

ES256 使用基于 P-256 曲线的 ECDSA。与 RS256 一样,它也是非对称的。

signature = ECDSA_SHA256(
  base64urlEncode(header) + "." + base64urlEncode(payload),
  privateKey
)

验证使用 ECDSA P-256 公钥。ES256 生成的签名要小得多(64 字节,而 RSA-2048 为 256 字节),并且验证速度更快。

算法对比

| 属性 | HS256 | RS256 | ES256 | |----------|-------|-------|-------| | 密钥类型 | 对称(共享密钥) | 非对称(RSA 密钥对) | 非对称(EC 密钥对) | | 签名大小 | 32 字节 | 256 字节 | 64 字节 | | 密钥分发 | 秘密共享 | 公钥是公开的 | 公钥是公开的 | | 轮换 | 一般 | 简单(JWKS) | 简单(JWKS) | | 使用场景 | 受信任的内部系统 | 分布式 API | 移动端、IoT |

常见验证陷阱

  • 算法混淆攻击。 如果您的代码未经强制校验就读取令牌中的 alg,攻击者可以更改算法。务必验证算法与预期值匹配。
  • 接受 alg: "none" 有些库接受未签名的令牌。请显式拒绝 alg: "none"
  • 签名截断。 非常量时间的比较可能会通过时序攻击泄露信息。请使用您的 JWT 库内置的比较功能。

在线验证 JWT 令牌

使用 JWT 解码器工具来解码和验证任何令牌。粘贴令牌,查看 header 和 payload,并通过提供相应的密钥或公钥来验证签名。所有处理都在您的浏览器中完成。

如何验证 JWT 签名:HS256、RS256 和 ES256 - CoolTool