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 | 对称密钥够用,性能最佳 |
| 分布式 / 微服务 / 公开 API | RS256 | 公钥可分发,私钥不泄露 |
| 移动端 / IoT / 高性能 | ES256 | 签名小、速度快、安全性高 |
| 金融 / 医疗等高安全 | PS256 | RSA-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
安全要点
- 禁用 none 算法:永远不要接受 alg: none 的 token,攻击者可伪造任意内容。
- 验证时显式指定算法:防止算法混淆攻击(攻击者将 RS256 改为 HS256,用公钥当对称密钥验证)。
- 密钥强度:RSA 至少 2048 位,HMAC 至少 256 位,ECDSA 至少 P-256。
- 定期轮换密钥:每 6-12 个月轮换,支持多密钥验证实现平滑过渡。
追问:HS256 和 RS256 的核心区别是什么?
HS256 是对称算法,签名和验证用同一密钥,任何能验证 token 的人也能伪造它;RS256 是非对称算法,私钥签名、公钥验证,验证方无法伪造。因此在微服务架构中,RS256 是唯一合理选择——你不会想把签名密钥交给每一个验证服务。
追问:什么是算法混淆攻击?
攻击者拿到 RS256 的公钥后,将 token header 的 alg 改为 HS256,再用该公钥作为 HMAC 密钥签名。如果服务端验证时没有显式指定算法,就会用公钥当 HMAC 密钥去验证,结果验证通过——攻击者成功伪造 token。防御方法:验证时必须通过 algorithms 参数显式指定允许的算法。