LabHub
学习 学习路径 课程

Keycloak 与企业认证

不问服务器也能校验 JWT

在 LabHub 中继续学习

一句话总结

JWT验证不是向认证服务器询问,而是用公钥验证签名并检查索赔的本地运算。所以很快。

概念图: 其他服务用的代币通过 · aud遗漏是最常见的。 · 旋转期间旧键和新键必须同时有效

为什么需要这个?

确认令牌是否有效的方法有两种。询问认证服务器的introspection端点,或验证令牌本身的签名。

前者是正确的。刚刚取消的代币也会立即失效。相反,每次请求都会产生网络往返。如果十个微服务分别询问,认证服务器就会出现瓶颈。

后者在没有网络的情况下结束。只要有公钥就可以了,公钥可以缓存。但不能立即反映取消。在令牌到期之前,它似乎是有效的。

实际操作的答案已经确定。将访问令牌设置为短时间(5~15分钟)进行本地验证。即将取消反映延迟限制为其寿命。如果同时使用刷新令牌循环,即使没有黑名单也能获得实质性无效化效果。

怎么行动

JWT分为三个部分。头,数据包,签名。前两个只是Base64URL编码,不是加密。任何人都可以读到。所以不能把秘密放在JWT中。

验证顺序如下。

首先是标题的algkid看。algnone如果与预期不同,将立即拒绝。实际上确实发生过没有确认这一点而导致签名被绕过的事故。

kid在JWKS端点中找到相应的公钥。JWKS是缓存的kid找不到的话,请重新更新一次——旋转键时需要。

验证签名。如果是RS256,则是通过公开密钥进行RSA验证。

接下来看一下索赔。exp(到期),iss(发行者是我们知道的那台服务器吗),aud(这个代币是为我们API准备的吗),还有如果需要的话nbfazp是。

aud遗漏检查的错误特别常见。同一个认证服务器发行的其他API用令牌会通过我们的API。

混淆ID令牌和访问令牌也很常见。ID令牌是为了客户端应用程序确认“用户是谁”而设的。aud是客户端。API调用必须使用访问令牌。

在现场相遇的样子

如果有键旋转,JWKS中会有多个键同时存在。kid这就是选择的原因。如果将缓存TTL设置太长,旋转后所有验证都会失败,如果设置太短,请求就会涌向认证服务器。一般设置几个小时的缓存。kid写下立即更新Ms.S的方式。

在验证中必须确认的五个事项

仅仅签名就可以了,你必须看全部五个。

索赔 确认 不看的话
iss 是我们知道的发行人吗 其他地方发行的代币通过
aud 针对这个服务吗 其他服务用的代币通过
exp 没有到期吗 永远有效
nbf 还是在有效期前吗 未来代币通过
签名 是否以发行者的指纹签名 伪造代币通过

**aud遗漏是最常见的。**使用相同认证服务器的不同应用程序的 可以使用访问令牌调用我们的API。如果有多个微服务的话 一定会确认的。

算法混淆攻击

不能把算法交给库。令牌头部的alg就那样相信的瞬间 攻击者把它none伊娜HS256换成发送。

정상: {"alg": "RS256", "kid": "abc"}   ← 공개키로 검증
공격: {"alg": "none"}                   ← 서명 없이 통과시키려는 시도
공격: {"alg": "HS256"}                  ← 공개키를 HMAC 비밀키로 쓰게 유도

第二个特别巧妙。RS256的公开密钥是任何人都知道的值,将其作为HMAC的 用对称密钥的话,攻击者可以制作有效的签名。

# ❌ 헤더를 믿는다
jwt.decode(token, key)

# ✅ 우리가 알고리즘을 정한다
jwt.decode(token, key, algorithms=["RS256"], audience="labhub-api",
           issuer="https://auth.labhub/realms/labhub")

键旋转和JWKS缓存

认证服务器会定期更换密钥。应用程序是kid符合(键识别符)的 选择公开密钥,不知道的kid遇到的话会再次收到JWKS。

1. 토큰 헤더의 kid 를 본다
2. 캐시에 있으면 그 키로 검증
3. 없으면 JWKS 엔드포인트를 다시 받는다 (여기에 속도 제한을 건다)

如果第3次没有限制的话,可以淹没**包含不知道的kid的请求,摧毁认证服务器。 有。**限制分堂几回,在此期间只写缓存的密钥。

现金寿命短(5~15分钟),但旋转期间旧键和新键必须同时有效。 做。这是认证服务器同时发布两个密钥的期间的原因。

下次实习要做的事情

从Keycloak中接收令牌,将其拆分为三个部分,确认索赔,在JWKS中找到密钥,验证签名,甚至确认数据包被篡改而验证失败。最后,比较100次inspector和本地验证所需的时间。