无'kid'头JWT签名验证方案咨询(密钥快速轮换、使用jwks-rsa)
实现思路
方案1:基于现有jwks-rsa库扩展盲试逻辑
- 不要直接调用
getSigningKey(<kid>)方法,先调用JwksClient实例暴露的getSigningKeys()方法拉取JWKS端点返回的全部有效签名密钥 - 对返回的密钥列表遍历,逐个取出公钥调用JWT验证库的验证方法尝试验证签名,只要有一个密钥验证通过即判定签名有效
- 补充优化点:
- 开启jwks-rsa的内置缓存功能,设置合理的缓存时长匹配IdP的密钥轮换周期,避免每次验证都重复拉取JWKS,降低请求开销
- 可以按密钥的
use字段过滤,仅保留use=sig的签名密钥,减少遍历次数 - 如果IdP返回的JWKS有
x5t之类的其他密钥标识,也可以和JWT头的对应字段做匹配优先试,进一步降低尝试次数
方案2:自定义JWKS拉取缓存逻辑
- 自行实现JWKS接口的定时拉取逻辑,把全量密钥缓存在本地,缓存刷新周期设置为比IdP密钥轮换周期短1/3左右,保证不会用到过期密钥
- 封装自己的
getValidatedSigningKey方法,内部遍历缓存的密钥列表做签名验证,返回验证通过的密钥 - 该方案灵活度更高,后续如果IdP支持kid了可以无缝切换回按kid匹配的逻辑
注意事项
- 盲试逻辑的性能损耗和JWKS返回的密钥数量正相关,一般IdP同一时间有效的签名密钥不会超过3个(轮换过渡期最多同时存在新旧2-3个),遍历开销完全可以忽略
- 要做好异常兜底,如果所有密钥都验证失败,直接返回签名无效的错误即可,不要做额外的降级逻辑避免安全风险
内容的提问来源于stack exchange,提问作者tpschmidt
相关产品推荐
相关产品推荐

