📋 What Is This Control?
Stop Server-Side Request Forgery (SSRF) is the control that makes sure when your server fetches a URL, it can only ever reach destinations you intended - never an internal service, a cloud metadata endpoint, or a local file. SSRF happens when an attacker supplies or influences a URL the application then requests on its behalf, turning your trusted server into a proxy that speaks from inside your network. The single most important idea: a URL is not safe just because its hostname looked safe - validate the resolved IP, pin it for the connection, and confine the request at the network layer.
Taxonomy note: SSRF was its own category (A10) in the 2021 web Top 10 and is folded into Broken Access Control in 2025 - but stopping it remains a distinct engineering discipline, so it stands alone here as C10.
🎯 What It Prevents
- Cloud Metadata Theft: Reaching
169.254.169.254to steal instance credentials - Internal Service Access: Admin panels, DBs, queues never meant to face the internet
- Internal Recon / Port Scanning: Mapping live hosts via timing & error differences
- Local File Disclosure:
file:///etc/passwd-style reads - Protocol Smuggling:
gopher:///dict://crafting raw requests to Redis/SMTP - DNS-Rebinding Bypass: A safe hostname re-pointed to a private IP after the check
- Redirect Bypass: A validated URL that 302s to a metadata/internal address
🧭 How It Works
↓
2. Resolve DNS and validate the RESOLVED IP (block private/link-local)
↓
3. Pin that exact IP for the connection (defeat DNS rebinding)
↓
4. Disable file/gopher/dict; do not follow redirects
↓
5. Confine at network egress + enforce IMDSv2; bound the response
❌ Without This Control
✅ Applying the Control
✓ Implementation Checklist
- Inventory every feature where the server fetches a user-influenced URL
- Validate each fetch against an allow-list of schemes, hosts, and ports (deny by default)
- Resolve DNS and validate the resolved IP; reject private/loopback/link-local/reserved (IPv4 & IPv6)
- Re-resolve and pin the IP at connection time to defeat DNS rebinding
- Disable non-HTTP schemes (
file,gopher,dict,ftp) - Block redirects, or re-validate every hop as strictly as the first
- Enforce network egress filtering so the app cannot reach internal ranges or the metadata IP
- Enforce IMDSv2 (session tokens) with a restrictive hop limit on cloud hosts
- Size-limit and time-limit upstream responses; never reflect them raw
- Run the fetching service with least-privilege IAM and strict timeouts
- Prefer a single centralized, vetted SSRF-safe HTTP client over raw
requests.get(url)
🔍 Tools & Techniques
Libraries & Platform:
Testing:
How to Verify:
- Try
file://,gopher://,dict://- all must be rejected - Point a controlled hostname at
127.0.0.1/169.254.169.254and confirm it is blocked - Test IP-encoding tricks:
127.1,2130706433,0x7f000001,[::1] - Set up a redirect to a private IP and confirm it is not followed
- Exercise DNS rebinding (answer changes between check and connect) and confirm the pin holds
🌍 Why It Matters
- Cloud metadata credential theft: The most damaging SSRF class - a user-influenced fetch is steered to the link-local metadata address
169.254.169.254and, where credentials are exposed without a session token, the instance role's temporary keys are retrieved and used against the cloud account. Session-token metadata services (IMDSv2) and hop limits exist precisely because of this incident class. - Reaching internal-only services: SSRF requests admin consoles, databases, caches, and orchestration APIs never exposed to the internet - because the request originates from a trusted internal source, "internal is safe" assumptions do not stop it.
- Internal recon & port scanning: Even without reading a response, timing and error differences let an attacker enumerate live hosts and open ports for a later attack.
- Local file & protocol abuse: Where the client honors extra schemes,
file://reads local files andgopher:///dict://smuggle crafted bytes to line-based internal services.
Described as documented incident classes, not tied to a specific vendor or CVE. The reliable control is IP-level validation after resolution plus network egress filtering - defense in depth, not a regex.
📌 Quick Tips
- DO NOT trust a URL because its hostname looked safe
- DO NOT rely on a block-list of
127.0.0.1/localhost- it is endlessly bypassable - DO NOT follow redirects on user-supplied fetches
- DO allow-list scheme + host + port, deny everything else
- DO validate the resolved IP and pin it for the connection
- DO block private/loopback/link-local ranges at the network egress too
- DO enforce IMDSv2 and size-limit every upstream response