离线静态检查工具,用于检查打包的 Java jar 中是否存在易受攻击的 netty-resolver-dns 版本,并检测 Spring WebClient 是否实际使用了 Netty 的 DNS 解析器。
针对 io.netty:netty-resolver-dns 中三个 DNS 缓存投毒 CVE 的离线检查工具——
CVE-2026-45674、CVE-2026-47691 和 CVE-2026-45673——并回答版本号无法回答的问题:
你的应用是否真的在使用该解析器?
如果你使用 Spring WebClient,答案很可能是肯定的,即使 netty-resolver-dns 并未出现在你的 pom.xml 中,代码中也没有任何地方调用它。
java -jar netty-resolver-dns-check-0.1.0.jar path/to/your-app.jar
netty-resolver-dns 经过四层传递引入:
spring-boot-starter-webflux
└ spring-boot-starter-reactor-netty
└ reactor-netty-http
└ reactor-netty-core
└ netty-resolver-dns (每一层:compile 作用域)
而且它不仅仅存在于 classpath 上。Reactor Netty 的 HttpClient——只要 Reactor Netty 存在,Spring Boot 就会为 WebClient 选择它作为连接器——使用 Netty 自己的 DnsAddressResolverGroup 解析主机名,而非 JDK 解析器:
HttpClientConfig.defaultAddressResolverGroup() → HttpResources.getOrCreateDefaultResolver()TcpResources.getOrCreateDefaultResolver() → NameResolverProvider.newNameResolverGroup(...)NameResolverProvider.newNameResolverGroup → new DnsNameResolverBuilder() → new DnsAddressResolverGroup(builder)(在 reactor-netty 1.1.x、1.2.x、1.3.x 和 main 中相同。)Spring Boot 的连接器检测顺序为 reactor > jetty > httpComponents > jdk,因此只要 Reactor Netty 存在,它就会胜出。
我们并未止步于阅读源码。在下面的受控实验中,一个默认的 Spring Boot 3.5.14 应用发起一次 WebClient 请求,通过 Netty 的解析器发送了一次真实的 DNS 查询(WRITE: UDP ... DefaultDnsQuestion(example.com.))。
CVE-2026-45674 和 CVE-2026-47691 的公告直言不讳: “任何使用 Netty DNS 解析器的应用都会受到影响。”
三个 CVE 共享相同的修复版本:4.1.135.Final(4.1 系列)/ 4.2.15.Final(4.2 系列)。
读取自各 spring-boot-dependencies-<v>.pom(<netty.version>),2026-09-11:
修复版本均已在 Maven Central 上。升级即可生效;本工具并不否认这一点。
版本号只是 mvn dependency:tree 中的一行。更难的问题是解析器是否真的在使用中。因此我们构建了四个真实的 Spring Boot 3.5.14 fat jar 和一个 3.5.16 的 jar,并测量了真实情况——Netty 实际发送的 DNS 查询——与本工具仅从 jar 推断的结果进行对比:
诚实的结果是:当没有覆盖时,工具可靠地报告 “使用中”,并且能在你的代码和配置中发现覆盖痕迹(3/3,对 A 无误报)。它无法区分 B(全部被覆盖)和 D(一个客户端仍使用默认值)——它们的字节码证据完全相同。因此,覆盖永远不会让它报告“不受影响”;它会报告“请手动检查”,并为你提供下面的一分钟检查方法。
--logging.level.io.netty.resolver.dns=DEBUG 启动你的应用WRITE: UDP——存在:Netty 的解析器正在使用;不存在:该请求未使用它不要以“DnsNameResolver 是否被加载?”作为判断依据。应用 B 替换了解析器,但仍然加载了该类,因为默认解析器组是急切构建的。只有查询日志行才是证据。
java -jar netty-resolver-dns-check-0.1.0.jar <directory | jar | war>
java -jar netty-resolver-dns-check-0.1.0.jar --version-of 4.1.132.Final
它读取实际打包的内容(fat jar 的 BOOT-INF/lib、war 的 WEB-INF/lib、嵌套 jar),而非 pom.xml——该依赖是传递性的,因此 pom 会告诉你它不存在。是否存在由类 io/netty/resolver/dns/DnsNameResolver.class 决定,而非版本清单。覆盖痕迹仅在你自己编写的类和 application*.properties/yml 中搜索(库 jar 自身会引用这些类)。
输出为中文;判定行和 CVE id 是脚本应作为依据的内容。
HttpClient 的库(例如网关)不会被单独分析——如果 Reactor Netty 存在,则假定使用默认路径。GitHub advisory API,读取于 2026-09-11:
Spring Boot 表位于 BootTable.java 中;一个单元测试会从中重新推导第 2 节中的三条陈述,因此表格与声明不会悄然偏离。
evidence/ 包含五个应用(A–E)和 runtime.sh,后者统计每个应用的 WRITE: UDP 行数。
tools/e2e_real_jars.py 对相同的五个 jar 运行本工具,并断言每个判定结果。
构建这些应用需要 Maven 3.6.3 或更高版本。
| 代码 | 含义 |
|---|---|
| 0 | 三个 CVE 均不适用(已修复版本) |
| 1 | 发现受影响版本——包括发现覆盖痕迹的情况 |
| 2 | 无法判断(未找到 netty-resolver-dns、未知版本、参数错误) |
| 4 | 某些文件无法读取——“我无法读取它”绝不能看起来像通过 |
Apache License 2.0
| Spring Boot | 默认 netty |
|---|
| 3.4.0 – 3.4.13 | 4.1.115 – 4.1.130 | 受影响——没有任何 3.4.x 版本包含修复 |
| 3.5.0 – 3.5.14 | 4.1.121 – 4.1.132 | 受影响 |
| 3.5.15 + | 4.1.135 | 已修复 |
| 4.0.0 – 4.0.6 | 4.2.7 – 4.2.12 | 受影响 |
| 4.0.7 + | 4.2.15 + | 已修复 |
| 应用 | 功能 | Netty DNS 查询(运行时) | 本工具(静态) |
|---|
| A | 默认 WebClient.builder() | 1 | 使用中 |
| B | .resolver(DefaultAddressResolverGroup.INSTANCE) | 0 | 发现覆盖 → 请手动检查 |
| C | spring.http.reactiveclient.connector=jdk | 0 | 发现覆盖 → 请手动检查 |
| D | 两个 WebClient,仅一个被覆盖 | 1 | 发现覆盖 → 请手动检查 |
| E | 默认,Spring Boot 3.5.16 | 1(在已修复的 4.1.135 上) | 不受影响 |
| CVE | GHSA | 严重性 | 受影响 → 已修复 |
|---|
| CVE-2026-45674 | GHSA-676x-f7gg-47vc | 高危 8.7 | <= 4.1.134.Final → 4.1.135.Final · 4.2.0–4.2.14 → 4.2.15.Final |
| CVE-2026-47691 | GHSA-5pvg-856g-cp85 | 高危 8.7 | 相同 |
| CVE-2026-45673 | GHSA-xmv7-r254-6q78 | 中危 6.8 | 相同 |