Spring Boot 4.1 InetAddressFilter: Source-Level SSRF Protection Against Internal IPs & Cloud Metadata

This article dissects Spring Boot 4.1's built-in InetAddressFilter, showing how a single @Bean declaration blocks SSRF attacks by validating resolved IPs against private, loopback, link-local (including 169.254.169.254 cloud metadata), and multicast ranges before TCP connections are made, with automatic integration across RestClient, RestTemplate, and WebClient.

Java Tech Workshop
Java Tech Workshop
Java Tech Workshop
Spring Boot 4.1 InetAddressFilter: Source-Level SSRF Protection Against Internal IPs & Cloud Metadata

SSRF Attack Surface and Why Traditional Defenses Fail

Server-Side Request Forgery (SSRF) lets attackers force a backend server to make arbitrary HTTP requests. Because the server often resides in a trusted network, it can reach internal databases, caches, management consoles, and cloud instance metadata services. The 2019 Capital One breach — 100 million records stolen — originated from an SSRF vulnerability that accessed AWS metadata at

http://169.254.169.254/latest/meta-data/iam/security-credentials/

to obtain temporary IAM credentials.

Common string-based blacklists (e.g., checking for 10., 192.168.) are easily bypassed:

DNS rebinding : attacker controls evil.com resolving first to a public IP (passes check), then to 127.0.0.1 (actual request hits localhost).

IP notation variants : 0x7f000001 (hex), 2130706433 (decimal), 0177.0.0.1 (octal) all represent 127.0.0.1.

IPv6 loopback : [::1].

Redirects : initial URL points to a benign domain; a 302 redirects to 169.254.169.254.

DNS rebinding (time-of-check vs time-of-use) : first resolution returns public IP, second returns private IP.

Correct defense requires: (1) resolve DNS first, then validate the actual IP; (2) re-validate on every redirect; (3) cover both IPv4 and IPv6; (4) include all special ranges — private, loopback, link-local, multicast, unspecified, reserved.

InetAddressFilter: Spring Boot 4.1's Built-In SSRF Guard

Spring Boot 4.1 introduces org.springframework.boot.http.client.InetAddressFilter, a functional interface:

@FunctionalInterface
public interface InetAddressFilter {
    boolean check(InetAddress address); // true = allow, false = deny
    static InetAddressFilter externalAddresses() { ... }
    static InetAddressFilter internalAddresses() { ... }
    static InetAddressFilter of(String... cidrs) { ... }
    default InetAddressFilter and(InetAddressFilter other) { ... }
    default InetAddressFilter andNot(String... cidrs) { ... }
    default InetAddressFilter or(InetAddressFilter other) { ... }
}

Declaring a @Bean of this type automatically applies it to all auto-configured HTTP clients: RestClient, RestTemplate, and WebClient (reactive).

What externalAddresses() Blocks — Source-Level Breakdown

externalAddresses()

is implemented as address -> !internalMatcher.matches(address), where internalMatcher is an InetAddressMatcher covering the following IPv4 CIDR blocks (per RFC 1918, RFC 3927, RFC 5735, RFC 3171, RFC 1112):

Loopback: 127.0.0.0/8 (localhost)

Private A: 10.0.0.0/8 (RFC 1918)

Private B: 172.16.0.0/12 (RFC 1918)

Private C: 192.168.0.0/16 (RFC 1918)

Link-local: 169.254.0.0/16 (cloud metadata services — AWS, Azure, GCP, Huawei Cloud)

Unspecified: 0.0.0.0/8 (all interfaces on host)

Multicast: 224.0.0.0/4 Reserved: 240.0.0.0/4 IPv6 equivalents:

Loopback: ::1/128 Link-local: fe80::/10 Unique local (RFC 4193): fc00::/7 Multicast: ff00::/8 Key insight : 169.254.169.254 (used by AWS, Azure, GCP, Huawei Cloud) falls inside 169.254.0.0/16, so externalAddresses() blocks it automatically.

Alibaba Cloud metadata at 100.100.100.200 resides in 100.64.0.0/10 (CGN shared address space), which is not covered by the default internal ranges. The article recommends adding .andNot("100.64.0.0/10") for Alibaba Cloud deployments.

Integration Point: DNS Resolution → IP Check → TCP Connect

The filter runs after DNS resolution but before TCP connection establishment . Flow:

1. Request: http://evil.com/path
2. DNS resolves evil.com → 169.254.169.254
3. InetAddressFilter.check(169.254.169.254) → false (link-local)
4. Exception thrown; request never sent

This defeats DNS rebinding because the check uses the resolved IP, not the hostname string.

Redirect Handling

On a 302 redirect, the client re-resolves the new Location header and runs the filter again:

1. Request http://evil.com/redirect → resolves to 1.2.3.4 (public, allowed)
2. Server responds 302 Location: http://169.254.169.254/
3. DNS resolves 169.254.169.254 → 169.254.169.254
4. InetAddressFilter.check(169.254.169.254) → false
5. Redirect aborted

Configuration Patterns

Allow only public internet (most common)

@Bean
public InetAddressFilter httpClientInetAddressFilter() {
    return InetAddressFilter.externalAddresses();
}

Whitelist: only specific internal subnet

return InetAddressFilter.of("192.168.1.0/24");

Blacklist: public internet but exclude a specific range

return InetAddressFilter.externalAddresses().andNot("203.0.113.0/24");

Allow public + specific internal subnet

return InetAddressFilter.externalAddresses()
    .or(InetAddressFilter.of("192.168.1.0/24"));

Allow subnet but exclude individual hosts

return InetAddressFilter.of("192.168.1.0/24")
    .andNot("192.168.1.1", "192.168.1.10");

Custom logic (e.g., resolve allowed hostnames at startup)

return address -> {
    Set<InetAddress> allowed = Set.of(
        InetAddress.getByName("api.example.com"),
        InetAddress.getByName("cdn.example.com")
    );
    return allowed.contains(address);
};

Cloud Metadata Coverage

AWS: 169.254.169.254 — ✅ blocked (link-local)

Azure: 169.254.169.254 — ✅ blocked

GCP: metadata.google.internal (resolves to 169.254.169.254) — ✅ blocked

Huawei Cloud: 169.254.169.254 — ✅ blocked

Alibaba Cloud: 100.100.100.200 — ⚠️ not blocked by default (in 100.64.0.0/10 CGN block)

Tencent Cloud: metadata.tencentyun.com — depends on resolved IP

Defense-in-Depth: IMDSv2

AWS recommends IMDSv2 (Instance Metadata Service Version 2), which requires a PUT request to obtain a session token before metadata can be read. Since SSRF payloads are typically GET-only, IMDSv2 mitigates metadata theft at the platform layer. InetAddressFilter provides a second, application-layer barrier — defense in depth.

Caveats and Limitations

Only protects HTTP clients created via Spring Boot auto-configuration ( RestClient.Builder, RestTemplateBuilder, WebClient.Builder). Manually instantiated clients (e.g., new RestTemplate(), raw HttpClient) are not covered.

Alibaba Cloud (and any provider using CGN space 100.64.0.0/10) requires explicit .andNot("100.64.0.0/10").

The filter is one layer; combine with network security groups, IMDSv2, input validation, and least-privilege IAM roles.

Security is not a single component's job — it's defense in depth. InetAddressFilter is an application-layer fence; pair it with network-layer, cloud-platform-layer, and architecture-layer controls to truly stop SSRF. Since Spring Boot now ships a one-line solution, there's no reason not to enable it.
Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

defense in depthSpring Boot 4.1SSRF protectionInetAddressFiltercloud metadata securityHTTP client security
Java Tech Workshop
Written by

Java Tech Workshop

Focused on Java backend technologies, sharing fundamentals, multithreading, JVM, the Spring ecosystem, microservices, distributed systems, high concurrency, source‑code analysis, and practical experience. Continuously delivers high‑quality original content, interview guides, and learning roadmaps to help Java developers progress from beginner to advanced, enhancing technical skills and core competitiveness.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.