
패키징된 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를 사용한다면, pom.xml 어디에도 netty-resolver-dns가 없고 코드에서 이를 호출하는 곳이 없더라도 답은 거의 확실히 "예"입니다.
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 scope)
그리고 이는 단지 클래스패스에만 있는 것이 아닙니다. Reactor Netty의 HttpClient — Reactor Netty가 존재할 때마다 Spring Boot가 WebClient를 위해 선택하는 커넥터 — 는 JDK 리졸버가 아닌 Netty 자체의 DnsAddressResolverGroup으로 호스트 이름을 해석합니다:
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가 있으면 항상 Reactor Netty가 우선합니다.
우리는 소스 코드를 읽는 데서 그치지 않았습니다. 아래의 통제된 실험에서, WebClient 요청을 한 번 보내는 기본 Spring Boot 3.5.14 앱은 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 기반 하나를 만들어, Netty가 실제로 전송한 DNS 쿼리라는 실측값을 이 도구가 jar만으로 추론하는 것과 비교했습니다:
정직한 결과: 이 도구는 오버라이드가 없을 때 "사용 중" 이라고 안정적으로 말하며, 코드와 설정에서 오버라이드 흔적을 찾아냅니다(3/3, A에 대한 오탐 없음). B(모두 오버라이드됨)와 D(하나의 클라이언트가 여전히 기본값 사용)를 구분할 수 없습니다 — 이들의 바이트코드 증거는 동일합니다. 따라서 오버라이드는 결코 "영향 받지 않음"이라고 말하게 하지 않으며, "수동 확인"이라고 말하고 아래의 1분 확인 방법을 제공합니다.
--logging.level.io.netty.resolver.dns=DEBUG로 앱을 시작합니다WRITE: UDP를 찾습니다 — 있으면: Netty의 리졸버가 사용 중, 없으면: 해당 요청은 이를 사용하지 않음"DnsNameResolver가 로드되었는가?"로 판단하지 마십시오. 앱 B는 리졸버를 교체했지만 여전히 해당 클래스를 로드했습니다. 기본 리졸버 그룹이 즉시(eagerly) 생성되기 때문입니다. 오직 쿼리 라인만이 증거입니다.
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
pom.xml이 아닌 실제로 패키징된 것(fat jar BOOT-INF/lib, war WEB-INF/lib, 중첩 jar)을 읽습니다 — 의존성은 전이적이므로 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)과 앱별 WRITE: UDP 라인을 세는 runtime.sh가 있습니다. 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 | 동일 |