Apache Camel 的 KeycloakSecurityPolicy 不会根据所配置的领域验证 JWT 令牌的 iss(颁发者)声明。这意味着由某个 Keycloak 领域签发的令牌会被配置给完全不同的领域的策略静默接受,从而破坏租户隔离。
受影响版本:4.15.0、4.16.0、4.17.0
跟踪地址:https://issues.apache.org/jira/browse/CAMEL-22854
在 KeycloakSecurityHelper.parseAccessToken() 中,当未显式提供公钥(这是默认配置)时,令牌仅从 Base64 解码——从未被验证:
public static AccessToken parseAccessToken(String tokenString, PublicKey publicKey)
throws VerificationException {
if (publicKey != null) {
return TokenVerifier.create(tokenString, AccessToken.class)
.publicKey(publicKey)
.verify()
.getToken();
} else {
// no signature verification, no issuer check
return TokenVerifier.create(tokenString, AccessToken.class).getToken();
}
}
由于默认代码路径会命中 else 分支,以下三项均未检查:
iss 声明从未与预期的 {serverUrl}/realms/{realm} 进行比较角色检查仍然通过,因为它只查看令牌载荷中的角色名称。如果角色名称(tenant-user)恰好跨领域匹配——这在多租户环境中很常见——请求就会通过。
在多租户应用中,每个租户对应一个独立的 Keycloak 领域时,租户 A 的用户可以访问为租户 B 保护的路由。具体来说:
本项目使用 Apache Camel 4.17.0 和本地 Keycloak 实例演示该漏洞。
jbang app install camel@apache/camel)camel infra 在底层使用)启动本地 Keycloak:
camel infra run keycloak
这会在 localhost:8080 上启动 Keycloak,管理员凭据为 admin/admin。
mvn verify
集成测试(CrossRealmTokenBypassIT)执行以下操作:
acme 和 globextenant-user 领域角色acme 领域中创建用户 alice(密码 alice123),并为其分配 tenant-user 角色globex 领域的 KeycloakSecurityPolicy 保护的 Camel 路由(direct:globex-protected)acme 领域获取 alice 的 JWT 令牌(颁发者:http://localhost:8080/realms/acme)请求成功。acme 令牌未出现任何错误即通过了 globex 策略。
| 测试 | 结果 | 含义 |
|---|---|---|
testAcmeTokenIsObtainable | 通过 | 健全性检查——令牌获取正常 |
在已修补的版本(4.18.0+)上,第二个测试将失败,第三个测试将通过。
camel infra stop keycloak
测试还会在 @AfterAll 中移除 acme 和 globex 领域。
KeycloakSecurityPolicy 应拒绝 iss 声明与 {serverUrl}/realms/{realm} 不匹配的令牌。该修复由 CAMEL-22854 跟踪,并将随 Camel 4.18.0(下一个 LTS 版本)一起发布。
testCrossRealmTokenAccepted |
| 通过 |
| 确认漏洞存在 |
testCrossRealmTokenShouldBeRejected | 失败 | 记录正确行为——在 4.17.0 上未抛出异常 |