5月27日 23:11

JWT 的签名算法有哪些,如何选择?

签名算法分三类

JWT 签名算法分为三类:HMAC 对称签名(HS256/HS384/HS512)、RSA 非对称签名(RS256/RS384/RS512/PS256/PS384/PS512)、ECDSA 椭圆曲线签名(ES256/ES384/ES512)。

如何选择

默认选 RS256。它是非对称算法,私钥签名、公钥验证,公钥可安全分发给任何需要验证 token 的服务,适合分布式架构和微服务场景。如果只是单体应用、内部系统通信,HS256 足够且性能更好。如果对签名体积或性能敏感(移动端、IoT),选 ES256——安全性与 RS256 相当,签名体积小约 50%。

场景推荐算法原因
单体应用 / 内部通信HS256对称密钥够用,性能最佳
分布式 / 微服务 / 公开 APIRS256公钥可分发,私钥不泄露
移动端 / IoT / 高性能ES256签名小、速度快、安全性高
金融 / 医疗等高安全PS256RSA-PSS 提供更强安全证明

代码示例

javascript
// HS256 — 对称密钥 const token = jwt.sign(payload, 'secret-key', { algorithm: 'HS256' }); const decoded = jwt.verify(token, 'secret-key', { algorithms: ['HS256'] }); // RS256 — 非对称密钥 const privateKey = fs.readFileSync('private.key'); const publicKey = fs.readFileSync('public.key'); const token = jwt.sign(payload, privateKey, { algorithm: 'RS256' }); const decoded = jwt.verify(token, publicKey, { algorithms: ['RS256'] }); // ES256 — 椭圆曲线 const ecPrivate = fs.readFileSync('ec-private.key'); const ecPublic = fs.readFileSync('ec-public.key'); const token = jwt.sign(payload, ecPrivate, { algorithm: 'ES256' }); const decoded = jwt.verify(token, ecPublic, { algorithms: ['ES256'] });

密钥生成

bash
# RSA 密钥对 openssl genrsa -out private.key 2048 openssl rsa -in private.key -pubout -out public.key # ECDSA 密钥对 openssl ecparam -name prime256v1 -genkey -noout -out ec-private.key openssl ec -in ec-private.key -pubout -out ec-public.key

安全要点

  1. 禁用 none 算法:永远不要接受 alg: none 的 token,攻击者可伪造任意内容。
  2. 验证时显式指定算法:防止算法混淆攻击(攻击者将 RS256 改为 HS256,用公钥当对称密钥验证)。
  3. 密钥强度:RSA 至少 2048 位,HMAC 至少 256 位,ECDSA 至少 P-256。
  4. 定期轮换密钥:每 6-12 个月轮换,支持多密钥验证实现平滑过渡。

追问:HS256 和 RS256 的核心区别是什么?

HS256 是对称算法,签名和验证用同一密钥,任何能验证 token 的人也能伪造它;RS256 是非对称算法,私钥签名、公钥验证,验证方无法伪造。因此在微服务架构中,RS256 是唯一合理选择——你不会想把签名密钥交给每一个验证服务。

追问:什么是算法混淆攻击?

攻击者拿到 RS256 的公钥后,将 token header 的 alg 改为 HS256,再用该公钥作为 HMAC 密钥签名。如果服务端验证时没有显式指定算法,就会用公钥当 HMAC 密钥去验证,结果验证通过——攻击者成功伪造 token。防御方法:验证时必须通过 algorithms 参数显式指定允许的算法。

标签:JWT