1
0
Files
2026-08-29 09:31:09 +05:30
..
2026-08-29 09:31:09 +05:30
2026-08-29 09:31:09 +05:30
2026-08-29 09:31:09 +05:30
2026-08-29 09:31:09 +05:30
2026-08-29 09:31:09 +05:30

ssrf — SSRF mitigation with InetAddressFilter, vulnerable endpoint included

Companion project for HTTP Client SSRF Mitigation in Spring Boot 4.1: The InetAddressFilter Everyone Will Configure Backwards on ankurm.com.

One application, one deliberately vulnerable endpoint, and five filter configurations selected by Spring profile. Every transcript under docs/output/ came from running it; ./scripts/run-all.sh regenerates all of them.

Versions

Version Notes
JDK 25 (Temurin 25.0.4.1+1) current LTS
Spring Boot 4.1.1 4.1.0 GA was 10 June 2026; InetAddressFilter is @since 4.1.0
Spring Framework 7.0.9 Boot-managed
Apache HttpComponents 5.6.4 on the classpath deliberately — see chapter 4
Tomcat 11.0.24
JUnit Jupiter / AssertJ Boot-managed 4 assertions

Versions were read from repo1.maven.org/.../maven-metadata.xml, not from release announcements.

Quickstart

./scripts/run.sh                 # no filter bean at all
./scripts/exploit.sh             # four targets, four sets of credentials

./scripts/run.sh docsfilter      # InetAddressFilter.externalAddresses()
./scripts/exploit.sh             # internal targets blocked, example.com still works

./scripts/run.sh blocklist       # the inversion
./scripts/exploit.sh             # RFC 1918 target succeeds, example.com fails

./scripts/run-all.sh             # regenerate everything under docs/output/

Profiles

Profile Filter bean What it shows
(none) the exploit, working
docsfilter externalAddresses() the reference documentation's recommendation, and it is correct
blocklist of(RFC1918) the release notes' word "block", acted on: attack succeeds, legitimate call fails
negated internalAddresses().negate() looks equivalent to externalAddresses(), differs on four rows
allowlist externalAddresses().and(of(...)) naming your destinations, and what that costs when their DNS changes
twofilters two beans the context does not start, and the diagnostic blames RestClient

Endpoints

Endpoint Purpose
GET /preview?url= the vulnerable fetcher
GET /internal/credentials the thing that must not be reachable
GET /diag/filter?host= what the running context decided. Delete before shipping

Documentation

  1. What SSRF actually costs you
  2. The exploit, start to finish
  3. matches means allow — the one that matters
  4. Where the filter runs depends on your HTTP client
  5. Wiring it up, and the three ways it silently does nothing
  6. Operating it
  7. Composing filters, and the vararg that matches nothing

Captured output

File Produced by
filter-matrix.txt FilterMatrix — 15 addresses × 9 filters
exploit-by-profile.txt run-all.sh — five profiles, five targets each
and-varargs-trap.txt AndVarargsTrap
two-filter-beans.txt the twofilters startup failure
tests.txt WhereTheFilterRunsTests

The three findings worth carrying away

  1. matches means allow. The release notes say "block"; the reference documentation says "only allow". The second is right. Writing of(<ranges to forbid>) produces a filter that permits exactly what you meant to stop.
  2. internalAddresses().negate() is not externalAddresses(). They disagree on CGNAT space, 0.0.0.0, TEST-NET-1 and multicast — the negation allows all four.
  3. and("a", "b") matches nothing. Each address becomes a separate filter and they are ANDed. Wrap multiple addresses in of(...) first.