Keycloak 26.8.1 于 10 月 13 日发布,是一个以安全修复为主的补丁版:GitHub release 的「Security fixes」一栏共 22 条,其中 19 条带 CVE 号,另有 15 条列在「Weaknesses」。我把 Spring Boot 4.0.6 资源服务器接到 26.8.1 上试了一遍,验 token 的链路不用改代码。官方博客和 GitHub release 都列出了完整 Security fixes 与 Weaknesses,下面按模块挑几类说。
修了哪些
管理端细粒度权限(FGAP)占了好几条。CVE-2026-94000 的标题是:只有 manage-users 的委派管理员,可以通过加入一个映射了管理员角色的组,变成 realm-admin。CVE-2026-94001 和 CVE-2026-97177 都是被拒绝了 reset-password 权限,却能通过别的接口绕过去。CVE-2026-97311 是 Admin REST API 的 roles/{role-name}/groups 会返回调用者看不到的组,官方注明这是 26.7.2 那次加固没修全。
认证流程这块,CVE-2026-94218 是通过 /realms/{realm}/login-actions/restart 绕过 2FA 设置强制。CVE-2026-96445 是 Conditional OTP 在不受信的代理路径上也信任了跳过用的请求头。CVE-2026-103884 是 X.509 CRL Distribution Point 的路径穿越,客户端提供的 CRL 路径能跳出 $KC_HOME/conf。
OIDC 和身份代理相关的有:CVE-2026-18569,签名校验关闭时,OIDC broker 的后端登出会接受未签名的伪造 logout token;CVE-2026-96446,PAR 的一次性限制可以用 prompt=none 绕过;CVE-2026-97846,Standard Token Exchange 在没带客户端证书时,绕过了要求的 mTLS 证书绑定,照样签出可用的 bearer token。
还有几条值得点名。CVE-2026-94215 是 RealmCacheSession 里按请求的会话级缓存导致跨 realm 读写。CVE-2026-107121 是 SMTP 开了 starttls=true 仍可能被降级成明文发送凭据和邮件。CVE-2026-101333 是 broker 登录端点的 idp 标签会无限制地创建指标序列。
依赖方面升级了 jackson-databind(com.fasterxml.jackson.core 和 tools.jackson.core 两条线)和 simple-git。
「Weaknesses」一栏的条目没有 CVE 号,其中 #53354 需要单独说。按官方标题,SAML broker 的 HTTP-Redirect 绑定在校验查询串签名时,校验的 SAMLResponse 和实际处理的不是同一个(利用 ; 参数分隔符),结果是可以免认证、以任意用户身份登录。受影响的是配置了 SAML 身份提供方的实例,这一条目前没有 CVE 号。同一栏里还有 #53116,SAML SLO 可以通过 LogoutRequest 的 SessionIndex 免认证地终止任意会话。
release notes 只给了 CVE 号和标题,没有标严重级别。
升级后 Spring Boot 资源服务器照常工作
之前那篇讲 Keycloak 26.8 的文章 聊的是新功能。这次是补丁版,我关心的是升上去以后,现有的 Spring Boot 资源服务器还能不能正常验 token,就在本机试了一下。
环境是 OpenJDK 21.0.12.1,构建用项目自带的 Maven Wrapper(Maven 3.10.0)。Keycloak 用官方 zip 包,dev 模式跑在 8180 端口:
KC_BOOTSTRAP_ADMIN_USERNAME=admin KC_BOOTSTRAP_ADMIN_PASSWORD=admin bin/kc.sh start-dev --http-port=8180
启动日志里能看到版本:
2026-10-14 08:06:46,245 INFO [io.quarkus] (main) Keycloak 26.8.1 on JVM (powered by Quarkus 3.40.1) started in 10.844s. Listening on: http://localhost:8180
用 kcadm 建一个 realm 和一个走 client credentials 的客户端:
bin/kcadm.sh config credentials --server http://localhost:8180 --realm master --user admin --password admin
bin/kcadm.sh create realms -s realm=demo -s enabled=true
bin/kcadm.sh create clients -r demo -s clientId=orders-job -s secret=orders-secret -s serviceAccountsEnabled=true -s publicClient=false -s standardFlowEnabled=false
Spring Boot 项目从 start.spring.io 生成,Boot 4.0.6,依赖只选了 web 和 oauth2-resource-server。配置就三行,issuer-uri 指向 demo realm:
spring.application.name=api
server.port=8081
spring.security.oauth2.resourceserver.jwt.issuer-uri=http://localhost:8180/realms/demo
HelloController 把 token 里的 azp 和签发方打出来:
package com.example.api;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.security.oauth2.jwt.Jwt;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class HelloController {
@GetMapping("/hello")
public String hello(@AuthenticationPrincipal Jwt jwt) {
return "hello " + jwt.getClaimAsString("azp") + ", issuer=" + jwt.getIssuer();
}
}
打包启动:
./mvnw -q -DskipTests package
java -jar target/api-0.0.1-SNAPSHOT.jar
2026-10-14T08:07:26.458+08:00 INFO 784789 --- [api] [ main] o.s.boot.tomcat.TomcatWebServer : Tomcat started on port 8081 (http) with context path '/'
2026-10-14T08:07:26.467+08:00 INFO 784789 --- [api] [ main] com.example.api.ApiApplication : Started ApiApplication in 1.506 seconds (process running for 1.86)
然后分三次请求:不带 token、带 Keycloak 签发的 token、带一个末尾多加了一个字符的 token。
curl -s -o /dev/null -w "%{http_code}\n" http://localhost:8081/hello
TOKEN=$(curl -s -d grant_type=client_credentials -d client_id=orders-job -d client_secret=orders-secret http://localhost:8180/realms/demo/protocol/openid-connect/token | jq -r .access_token)
curl -s http://localhost:8081/hello -H "Authorization: Bearer $TOKEN"; echo
curl -s -o /dev/null -w "%{http_code}\n" http://localhost:8081/hello -H "Authorization: Bearer ${TOKEN}x"
输出:
401
hello orders-job, issuer=http://localhost:8180/realms/demo
401
没 token 是 401。正常 token 能过,azp 是 orders-job。签名被改坏的 token 也是 401。资源服务器这一侧不用改代码,换掉 Keycloak 就行。
这个小实验只覆盖了 client credentials 加 JWT 校验这条链路。上面那些 CVE 大多落在管理端权限、身份代理、2FA 和 SAML 上,这里没有逐条复现。
原文:Keycloak 26.8.1 released,完整修复列表见 GitHub release 26.8.1。
一起交流
分享你的思考,让讨论更进一步。