Google 开源的 Java 智能体框架 ADK Java 在 1.13.0(2026-10-21)里收紧了 dev 服务器:跨域请求默认只放同源,还新增了一道 DNS rebinding 防护。用同一份启动代码分别跑 1.12.0 和 1.13.0,带陌生 Origin 的请求从 200 加 Access-Control-Allow-Origin: * 变成了 403。dev README 同时写明,AdkWebServer.start() 和 main() 不设 server.address,这时 Host 检查不开启:实测进程监听在所有地址上,只伪造 Host、不带 Origin 的 GET 仍然回 200,按 README 设上 server.address=127.0.0.1 后才被拦下。
项目仓库:github.com/google/adk-java,许可证 Apache License 2.0。dev 模块是 com.google.adk:google-adk-dev,1.13.0 依赖 Spring Boot 4.0.2。上一个版本的工作流图改动见之前的 ADK Java 1.12.0 介绍。
主源:
- ADK Java v1.13.0 Release
- 5667d38 fix(dev): allow only same-origin requests by default for CORS and /run_live
- 1ae5078 fix(dev): reject DNS-rebinding requests that reach the server over loopback
- dev/README.md(v1.13.0)
安全公告状态:这两条列在 release notes 的 Bug Fixes 下,没有对应 CVE 编号;GitHub 仓库的 security advisories 接口返回 0 条,官方也没有给严重级别。
改了什么
1.12.0 的 AdkWebCorsProperties 在没配 adk.web.cors.origins 时回落成 *。5667d38 把默认值改成空列表,也就是只放同源,REST API 和 /run_live WebSocket 都一样。dev UI 由服务自己托管,属于同源,不用额外配置。另起端口跑的前端要显式列出来:
adk.web.cors.origins=http://localhost:4200
5667d38 还把 Forwarded / X-Forwarded-* 默认按 server.forward-headers-strategy=framework 处理。提交说明和 README 都提醒,反向代理要剥掉或覆盖不可信客户端带来的这些头,也可以设成 none 忽略它们。
1ae5078 新增了 OriginGuardFilter。README 里写了两条规则:server.address 是回环地址时,拒绝非回环的 Host;请求经回环到达时,拒绝非回环的 Origin。adk.web.cors.origins 里列出的来源及其主机不受限,配成 * 则两条都关掉。mvn google-adk:web 默认绑回环;AdkWebServer.start() 和 main() 不设 server.address,README 让这种情况自己设 server.address=127.0.0.1 来打开 Host 检查。
对照环境
JDK 21.0.12.1,Maven 项目只依赖 google-adk 和 google-adk-dev,用 -Dadk.version 切 1.12.0 / 1.13.0。启动方式是 README 提到的 AdkWebServer.start(),不调用模型,只请求 /list-apps:
LlmAgent agent = LlmAgent.builder()
.name("hello_agent")
.description("Demo agent for the dev server")
.model("gemini-2.5-flash")
.instruction("Reply briefly.")
.build();
AdkWebServer.start(agent);
端口固定 18085,五种请求依次记为 A 到 E:
# A 不带 Origin
curl -s -D - http://127.0.0.1:18085/list-apps
# B 跨域 Origin
curl -s -D - -H 'Origin: http://evil.example' http://127.0.0.1:18085/list-apps
# C 伪造 Host,Origin 与之一致
curl -s -D - -H 'Host: rebind.example:18085' -H 'Origin: http://rebind.example:18085' http://127.0.0.1:18085/list-apps
# D 只伪造 Host,不带 Origin
curl -s -D - -H 'Host: rebind.example:18085' http://127.0.0.1:18085/list-apps
# E 另起前端的来源
curl -s -D - -H 'Origin: http://localhost:4200' http://127.0.0.1:18085/list-apps
C、D 模拟 DNS rebinding:恶意域名被解析到本机后,浏览器发出的 Host 是那个域名,页面和服务算「同源」,CORS 本身拦不住。下面的输出由脚本整理,每条只保留状态行、Access-Control-Allow-Origin 头和响应体。
1.12.0:五种请求全部放行
摘录:
--- B cross-origin Origin
HTTP/1.1 200
Access-Control-Allow-Origin: *
body: ["hello_agent"]
--- C rebinding Host+Origin
HTTP/1.1 200
body: ["hello_agent"]
五种请求全部 200,跨域请求直接拿到 Access-Control-Allow-Origin: *。
1.13.0 默认:带 Origin 的被拦,不带 Origin 的取决于监听地址
摘录:
--- B cross-origin Origin
HTTP/1.1 403
body: Invalid CORS request
--- C rebinding Host+Origin
HTTP/1.1 403
body: Forbidden: origin not allowed
--- D rebinding Host, no Origin
HTTP/1.1 200
body: ["hello_agent"]
B 的响应体 Invalid CORS request 来自 Spring 的 CORS 处理。C 的 Host 和 Origin 一致,CORS 认为是同源,是新加的 OriginGuardFilter 按「经回环到达、Origin 非回环」这条规则拦下的。D 只改了 Host、没带 Origin,第二条规则管不到,仍是 200。按 MDN 对 Origin 请求头的说明,浏览器对同源的 GET、HEAD 请求一般不附加 Origin,D 对应的就是这种请求形态。
此时进程的监听地址:
LISTEN 0 100 *:18085 *:*
没有设 server.address,Tomcat 绑在所有地址上,Host 检查按设计不开启,这和 README 对 AdkWebServer.start() 的描述一致。
1.13.0 加 server.address=127.0.0.1
摘录:
--- C rebinding Host+Origin
HTTP/1.1 403
body: Forbidden: host not allowed
--- D rebinding Host, no Origin
HTTP/1.1 403
body: Forbidden: host not allowed
监听地址变成:
LISTEN 0 100 [::ffff:127.0.0.1]:18085 *:*
C 和 D 都在 Host 这一步被拦,正常的 127.0.0.1 请求(A)仍然 200。
另起前端要配白名单
1.13.0 默认下,Origin: http://localhost:4200 也是 403 Invalid CORS request。加上 -Dadk.web.cors.origins=http://localhost:4200 之后:
--- E allowed dev UI origin
HTTP/1.1 200
Access-Control-Allow-Origin: http://localhost:4200
body: ["hello_agent"]
白名单只放行列出的来源,evil.example 照样 403。
升级时要改的地方
- 代码里用
AdkWebServer.start()起 dev 服务的,按 README 加server.address=127.0.0.1(实测用的是系统属性-Dserver.address=127.0.0.1),Host检查才会打开。 - 前端单独跑在 4200 之类端口的,把来源写进
adk.web.cors.origins。README 建议避免*,配了两道检查都会关掉。 - 用
-Dhost=0.0.0.0启动的,1ae5078 的提交说明要求改用http://localhost:{port}打开 UI,http://0.0.0.0这个 origin 现在会被拒。 - 放在反向代理后面的,确认代理会剥掉或覆盖客户端带来的
Forwarded/X-Forwarded-*,因为 5667d38 之后默认会应用这些头。
一起交流
分享你的思考,让讨论更进一步。