Spring Boot中使用InetAddressFilter缓解HTTP客户端SSRF攻击
[LOADING...]
1. 引言
有时限制 HTTP 客户端允许调用的远程地址很有用。这有助于防止应用程序连接到非预期目的地,并为服务器端请求伪造(SSRF)攻击提供有效防御。为了限制 HTTP 客户端可以调用的地址,我们可以使用 InetAddressFilter。该过滤器决定哪些地址允许用于出站请求。 在本教程中,我们将探讨如何在基于 Spring 的应用程序中使用 InetAddressFilter 类。
2. 理解 HTTP 客户端的 SSRF
SSRF 是一种严重的安全漏洞,攻击者可以利用它强制服务器向非预期位置发起请求。这些请求可能针对内部系统、云元数据端点或回环地址。 为了演示 SSRF 如何在 HTTP 客户端中发生,我们考虑一个简单的端点,它接受一个 URL 并使用 RestClient 获取其内容:
该端点接受一个 URL 作为请求参数,并将其传递给 RestClient。其目的是允许用户从公共 URL 获取内容。例如,一个合法请求可能如下:
然而,攻击者可以提供指向内部资源的 URL:
我们的服务器不会联系公共网站,而是向其内部网络发起请求。这可能会暴露内部管理 API、数据库、Kubernetes 服务等。这类漏洞被称为 SSRF。从历史上看,在 Spring Boot 应用程序中缓解 SSRF 需要自定义 ClientHttpRequestInterceptor 实现或复杂的网络级防火墙。Spring Boot 4.1.0 通过 InetAddressFilter 接口为 SSRF 缓解提供了原生支持。
3. Maven 依赖
让我们首先将 spring-boot-starter-restclient 依赖添加到 pom.xml:
4. 介绍 InetAddressFilter
InetAddressFilter 接口是一个函数式接口。与验证主机名或 URL 不同,InetAddressFilter 在 DNS 解析之后执行检查。Spring Boot 为常见场景提供了几个内置过滤器,例如仅允许公共地址(InetAddressFilter.externalAddresses())或阻止特殊用途的网络范围(InetAddressFilter.internalAddresses())。我们可以使用逻辑运算符组合这些过滤器:
该 API 还支持 and()、or() 和 negate() 来组合更复杂的规则:
这允许我们构建富有表现力的允许列表和阻止列表,而无需自定义网络代码。
5. 全局应用过滤器
最简单且最常见的方法是声明一个 InetAddressFilter bean。 Spring Boot 随后会自动将其应用于每个自动配置的 HTTP 客户端构建器(RestClient、WebClient、RestTemplate 等)。让我们配置 InetAddressFilter 仅允许连接到外部 IP 地址:
有了这一个 bean,任何由自动配置构建器创建的客户端都会拒绝解析地址不是外部的请求。现在,我们可以在不进行额外安全配置的情况下创建 RestClient:
由于 RestClient.Builder 已经被 Spring Boot 定制,生成的 RestClient 会自动使用配置的 InetAddressFilter。因此,任何解析目标不是外部地址的请求(例如回环地址或私有 IP 地址)都会在建立连接之前被拒绝。这种方法将应用程序的出站网络策略集中在一个地方,并确保所有自动配置的 HTTP 客户端一致地执行相同的 SSRF 保护。
6. 按客户端应用过滤器
在某些场景下,全局应用 InetAddressFilter 可能限制过于严格。例如,一个应用程序可能同时与公共 API 和特定的内部服务通信,每个服务需要不同的出站访问规则。在这种情况下,我们可以在创建客户端时通过配置底层 HttpClientSettings 来将 InetAddressFilter 应用于单个 HTTP 客户端。让我们创建一个仅允许请求外部地址的 RestClient:
在这里,过滤器仅附加到这个特定的 RestClient 实例。应用程序中的其他 HTTP 客户端可以使用不同的安全策略或保持不受影响。这种方法在不同集成具有不同网络需求时非常有用。例如,用于调用第三方 API 的客户端可以限制为公共地址,而用于与受信任内部服务通信的另一个客户端可以使用更具体的允许列表。通过在客户端级别配置过滤器,我们可以应用细粒度的出站请求控制,而无需改变整个应用程序的行为。
7. 测试
为了验证所配置的 InetAddressFilter 是否生效,我们可以编写一个单元测试,使用配置好的 RestClient bean 访问回环地址。应用程序配置了 InetAddressFilter.externalAddresses(),它只允许公开可路由的 IP 地址。 由于 127.0.0.1 是回环地址,请求会在建立 HTTP 连接之前被拒绝。让我们创建一个测试用例:
当测试执行时,RestClient 解析目标主机,并将生成的 InetAddress 传递给配置的 InetAddressFilter。由于该地址不满足 externalAddresses() 策略,Spring Boot 会抛出 FilteredHostException,并在打开网络连接之前中止请求。
8. 结论
在本教程中,我们探讨了 SSRF 如何影响根据用户控制输入进行出站 HTTP 请求的应用程序。我们还了解了 Spring Boot 4.1 如何通过 InetAddressFilter 简化 SSRF 缓解。它允许应用程序在建立出站连接之前验证解析后的目标地址。该功能与 Spring 的 HTTP 客户端基础设施集成,包括阻塞式和响应式客户端,并提供了灵活的 API 来组合允许列表和阻止列表。通过应用 InetAddressFilter,我们可以在 HTTP 客户端级别强制实施出站网络访问策略。这降低了未经授权访问内部资源的风险,同时保持了应用程序代码的整洁和可维护性。
与往常一样,源代码可在 GitHub 上获取。