devslab-kr/ssrf-guard

GitHub: devslab-kr/ssrf-guard

面向 JVM 平台的 SSRF 防护库,通过四层过滤机制为多种 HTTP 客户端及 LLM Agent 工具调用提供统一的出站请求安全校验。

Stars: 0 | Forks: 0

# ssrf-guard **English** · [한국어](README.ko.md) [![Maven Central](https://img.shields.io/maven-central/v/kr.devslab/ssrf-guard.svg?label=Maven%20Central)](https://central.sonatype.com/artifact/kr.devslab/ssrf-guard) [![CI](https://static.pigsec.cn/wp-content/uploads/repos/cas/ad/ad5834178f7599af9fdda11629d49cae07f2997beec49821b2920eff5bfd50e7.svg)](https://github.com/devslab-kr/ssrf-guard/actions/workflows/ci.yml) [![codecov](https://codecov.io/gh/devslab-kr/ssrf-guard/branch/main/graph/badge.svg)](https://codecov.io/gh/devslab-kr/ssrf-guard) [![License](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](LICENSE) [![Java](https://img.shields.io/badge/Java-21+-orange.svg)](https://openjdk.org/projects/jdk/21/) [![Spring Boot](https://img.shields.io/badge/Spring%20Boot-3.5+-green.svg)](https://spring.io/projects/spring-boot) 📖 **[文档 → ssrf-guard.devslab.kr](https://ssrf-guard.devslab.kr/)** ## 可运行示例 这些是独立的 Spring Boot 项目,涵盖了下面记录的每个模块 —— 克隆,`./gradlew bootRun`,curl。无需复制粘贴;示例已经端到端配置好(包含冒烟测试)。 | 示例 | 展示内容 | | --- | --- | | [`ssrf-guard-demo`](https://github.com/devslab-kr/devslab-examples/tree/main/ssrf-guard-demo) | RestClient + RestTemplate + WebClient 全部通过同一个 `UrlPolicy` 配置。15 种模式攻击矩阵 endpoint,Micrometer 指标 | | [`ssrf-guard-springai-demo`](https://github.com/devslab-kr/devslab-examples/tree/main/ssrf-guard-springai-demo) | ⭐ LLM agent SSRF 防御。模拟 LLM 驱动,无需 API key | | [`ssrf-guard-feign-demo`](https://github.com/devslab-kr/devslab-examples/tree/main/ssrf-guard-feign-demo) | Spring Cloud OpenFeign `RequestInterceptor` 集成 | | [`ssrf-guard-jdkhttp-demo`](https://github.com/devslab-kr/devslab-examples/tree/main/ssrf-guard-jdkhttp-demo) | `java.net.http.HttpClient` 封装 —— 库本身不依赖 Spring | | [`ssrf-guard-okhttp-demo`](https://github.com/devslab-kr/devslab-examples/tree/main/ssrf-guard-okhttp-demo) | OkHttp `Interceptor` + `Dns` 集成 —— 同样不依赖 Spring | 完整索引请访问 [github.com/devslab-kr/devslab-examples](https://github.com/devslab-kr/devslab-examples)。 ## 模块矩阵 选择与你的 HTTP client 匹配的模块。核心库 (`ssrf-guard-core`) 将作为传递依赖被引入。 | 模块 | 用例 | 需要 Spring? | |---|---|---| | **`ssrf-guard`** | Meta artifact — RestClient + HttpClient5 (兼容 v2.0.0) | ✅ | | `ssrf-guard-restclient` | Spring 6.1+ `RestClient` | ✅ | | `ssrf-guard-resttemplate` | Spring `RestTemplate` | ✅ | | `ssrf-guard-webclient` | Spring WebFlux `WebClient` — URL 阶段过滤器 + reactor-netty DNS 阶段 IP 过滤器 (v3.1+) | ✅ | | `ssrf-guard-feign` | Spring Cloud OpenFeign | ✅ | | `ssrf-guard-llm` 🧩 | 与框架无关的 JSON tool-input 验证器 (v3.1+) — 被 LLM adapter 复用 | — | | **`ssrf-guard-springai`** ⭐ | Spring AI `ToolCallback` URL 验证 — 基于 `-llm` 的轻量级 adapter | ✅ | | **`ssrf-guard-langchain4j`** ⭐ | LangChain4j `ToolExecutor` URL 验证 — 为另一个 Java LLM 框架提供相同的防御 (v3.1+) | ✅ | | `ssrf-guard-httpclient5` | 直接使用 Apache HttpClient 5 | — | | `ssrf-guard-jdkhttp` | `java.net.http.HttpClient` | — | | `ssrf-guard-okhttp` | OkHttp | — | ## 功能说明 你的服务发出的每一个外部 HTTP 调用,在真正打开 socket 之前,都会经过一个四层 SSRF 过滤器: 1. **URL 阶段检查 (前线)** — scheme / host / port / IP-literal-form / userinfo 会在成本最低的关卡被拒绝,时机早于任何 DNS lookup。可捕获混淆 IP 的绕过攻击 (例如 `http://2130706433/` → `127.0.0.1`)。 2. **DNS 阶段白名单二次检查** — 在解析 hostname 时,会再次应用相同的 host policy。 3. **私有网络 IP 过滤器** — loopback, RFC-1918, link-local (包含位于 `169.254.169.254` 的 AWS metadata), CGNAT, IPv6 ULA,**IPv4 映射的 IPv6 + 6to4 解嵌** (`::ffff:10.0.0.5` 和 `2002:0a00::` 会被正确归类为私有地址)。 4. **重定向重新验证** — 每一次 3xx 跳转都会经过相同的检查。攻击者无法通过将 `example.com` 加入白名单,然后重定向到 `169.254.169.254`。 解析器验证过的那个 `InetAddress` 数组,正是 HttpClient 传递给 `Socket.connect()` 的数组 —— TOCTOU 时间窗口已关闭。 ## Spring AI tool calls — 全新的 SSRF 攻击面 将 URL 作为 tool 参数接收的 LLM agent 默认就是 SSRF 攻击向量: ``` @Tool("Fetch a URL") String fetchUrl(String url) { return restClient.get().uri(url).retrieve().body(String.class); // ↑ attacker controls the URL — one-line SSRF } ``` `ssrf-guard-springai` 会包装每一个 `ToolCallback`,使得 URL 形态的参数在 tool 运行前根据策略进行验证;如果拒绝,则会返回一个结构化的错误字符串,LLM 可以解析该字符串并从中恢复。 ``` ToolCallback[] raw = ToolCallbacks.from(new MyTools()); ToolCallback[] safe = SsrfGuardedToolCallbacks.wrap(raw, urlPolicy); ``` 自动配置会接管它 —— 任何 `@Bean ToolCallback` 都会通过 `BeanPostProcessor` 进行包装。 ## 安装 ### Maven ``` kr.devslab ssrf-guard 3.1.1 ``` ### Gradle (Kotlin DSL) ``` implementation("kr.devslab:ssrf-guard:3.1.1") ``` ## 配置 ``` ssrf: guard: enabled: true # master switch allowed-schemes: [ "http", "https" ] allowed-ports: [ -1, 80, 443 ] # -1 = default port for the scheme block-private-networks: true reject-ip-literal-hosts: true # NEW v3.0.0 — block http://127.0.0.1, http://2130706433, etc. reject-user-info: true # NEW v3.0.0 — block https://user:pass@host/... follow-redirects: true # Exact-match whitelist exact-hosts: - api.partner.com - billing.example.org # Suffix whitelist — `partner.com` covers `partner.com` AND any subdomain # of it, but not `badpartner.com` (label-boundary match). suffixes: - partner.com - example.org connect-timeout: 5s read-timeout: 10s ``` 一旦该 starter 加入 classpath,Spring Boot 为你构建的每一个 `RestClient` 都会自动应用该策略 —— 消费者端无需额外配置。 ## 用法 ``` @Service public class PartnerApi { private final RestClient client; public PartnerApi(RestClient.Builder builder) { this.client = builder.build(); } public Customer fetch(long id) { // Whitelisted host → goes through. Anything not on the list throws // SecurityException before the connection is opened. return client.get() .uri("https://api.partner.com/customers/{id}", id) .retrieve() .body(Customer.class); } } ``` 当请求未被列入白名单时会发生什么: ``` kr.devslab.ssrfguard.core.SsrfGuardException: Host not allowed: evil.com (reason=blocked_host, scheme=https, host=evil.com) at kr.devslab.ssrfguard.core.UrlPolicy.reject(...) ``` `SsrfGuardException extends SecurityException` — v2.0.0 版本的 `catch (SecurityException e)` 代码依然有效。你可以捕获新的异常类型来读取 `e.reason()` (一个 `BlockReason` 枚举:`blocked_host`, `blocked_private_ip`, `blocked_ip_literal`, `blocked_userinfo`, `blocked_scheme`, `blocked_port`, `blocked_redirect`)。 ## 可观测性 (通过 Micrometer 自动装配) ``` ssrf_guard_blocked_total{reason="blocked_private_ip", scheme="http"} 42 ssrf_guard_allowed_total{scheme="https"} 13042 ``` 此外,每次拦截都会产生一条结构化的 WARN 日志: ``` WARN k.d.s.core.UrlPolicy : ssrf-guard: Host not allowed: evil.com (reason=blocked_host, scheme=https, host=evil.com) ``` Tag 数量是有限的 (`reason` 是一个枚举,`scheme` 为 http/https) — Prometheus / Datadog / CloudWatch 可以轻松摄取。 ## 性能 interceptor 在允许路径上的开销为 **每请求约 5 μs** (JMH, JDK 21) — 对于 100 ms 的远程 API 调用而言,这仅是 **0.005% 的开销**,在实际应用中几乎不可见。 | 热点路径 | 平均开销 | 备注 | |---|---:|---| | `UrlPolicy.validate` 允许通过 | ~5 μs | 占生产环境流量的 99%+ | | `UrlPolicy.validate` 拦截 | 5-12 μs | 早期退出 (如 IP literal) 比晚期退出 (如白名单) 成本更低 | | `JsonToolInputGuard` 小型 JSON | ~6 μs | 包含一个 URL 的 LLM tool 输入 | | `JsonToolInputGuard` ~2 KB JSON | ~24 μs | 包含 3 个 URL 的 RAG 增强 tool 输入 | 完整的方法论、带标准差的各项具体数据及其解读方式:请参见 [`BENCHMARKS.md`](./BENCHMARKS.md)。你可以使用 `./gradlew :ssrf-guard-benchmarks:jmh` 自行复现。 ## 自动配置注册了什么 (RestClient 模块) 当 `ssrf.guard.enabled=true` (默认值) 时,RestClient 自动配置会被激活并注册以下内容: - `SafeDnsResolver` — 白名单 + 私有 IP 过滤器,接入到 Apache HttpClient 5 的连接管理器中 - `CloseableHttpClient` — 内置了该 resolver 并且 (启用重定向时) 包含 `SafeRedirectStrategy` - `HttpComponentsClientHttpRequestFactory` — 配置了指定的连接/读取超时时间 - `UrlPolicy` — 前线 URL 阶段关卡 (scheme, host, port, IP-literal, userinfo) - `SsrfGuardClientHttpRequestInterceptor` — 委托给该策略的 Spring `ClientHttpRequestInterceptor` - `SsrfGuardMetrics` — 当存在 `MeterRegistry` 时由 Micrometer 提供支持,否则为空操作 - `RestClientCustomizer` (命名为 `ssrfRestClientCustomizer`) — 将工厂和 interceptor 绑定到 Spring Boot 自动构建的 `RestClient.Builder` 上 每个模块都有自己的自动配置 — `SsrfGuardRestTemplateAutoConfiguration`, `SsrfGuardWebClientAutoConfiguration`, `SsrfGuardFeignAutoConfiguration`, `SsrfGuardSpringAiAutoConfiguration`。它们全都复用相同的 `UrlPolicy` 和 `SsrfGuardMetrics` bean。每个 bean 都是 `@ConditionalOnMissingBean`,因此你可以替换其中任何一个部分。 ## 环境要求 - Java 21+ - Spring Boot 3.5+ (适用于基于 Spring 的模块) - Spring AI 1.0+ (适用于 `springai` 模块) - Spring Cloud 2024.0+ (适用于 `feign` 模块) - Apache HttpClient 5 (由 `-httpclient5`, `-restclient`, `-resttemplate` 传递依赖引入) ## 许可证 Apache License 2.0 — 详见 [LICENSE](LICENSE) 和 [NOTICE](NOTICE)。 由 [Devslab](https://devslab.kr) 构建 · [DevsLab 开源工具包](https://github.com/devslab-kr) 的一部分。
标签:CISA项目, Spring Boot, SSRF防护, 域名枚举, 网络安全, 隐私保护