API Security in Depth · Function Authorization, Abuse & SSRF
Server-Side Request Forgery (SSRF)
When your API fetches URLs on a user's behalf.
Features like webhooks, URL previews, image import, PDF rendering and OIDC discovery make your server fetch user-supplied URLs. Attackers point them at internal services and cloud metadata (169.254.169.254) to steal credentials. Defenses: an allowlist of schemes/hosts where possible; otherwise resolve DNS and block private, loopback, link-local and metadata ranges (IPv4 and IPv6), connect to the resolved IP (prevents DNS rebinding), disable redirects or re-validate each hop, set timeouts and size limits, run fetchers in an isolated egress network, and use IMDSv2 on AWS.
SSRF defenses
| Defense | Stops | Gaps |
|---|---|---|
| Host allowlist | Almost everything | Not possible for arbitrary webhooks |
| Block private IP ranges after DNS resolution | Internal access | DNS rebinding unless you pin the IP |
| Pin resolved IP for the connection | DNS rebinding | Needs custom HTTP client |
| No redirects / revalidate hops | Redirect to internal | — |
| Isolated egress proxy / network | Lateral movement | Infra effort |
| IMDSv2 (AWS) | Simple metadata theft | Only cloud metadata |
import ipaddress, socket
BLOCKED = [ipaddress.ip_network(n) for n in (
"0.0.0.0/8", "10.0.0.0/8", "100.64.0.0/10", "127.0.0.0/8", "169.254.0.0/16",
"172.16.0.0/12", "192.168.0.0/16", "::1/128", "fc00::/7", "fe80::/10")]
def resolve_safe(url: str) -> tuple[str, str]:
u = urllib.parse.urlsplit(url)
if u.scheme != "https" or not u.hostname:
raise ValueError("https URLs only")
infos = socket.getaddrinfo(u.hostname, u.port or 443, proto=socket.IPPROTO_TCP)
ips = {ipaddress.ip_address(i[4][0]) for i in infos}
if any(ip in net for ip in ips for net in BLOCKED) or any(ip.is_private or ip.is_reserved for ip in ips):
raise ValueError("destination not allowed")
return u.hostname, str(next(iter(ips))) # connect to this IP with SNI/Host = hostname, no redirects
// Use a vetted library that validates the resolved address at connect time,
// e.g. `ssrf-req-filter` for Node's http(s) agent, and disable redirects.
import ssrfFilter from "ssrf-req-filter";
const res = await axios.get(url, { httpsAgent: ssrfFilter(url), maxRedirects: 0, timeout: 5000, maxContentLength: 5e6 });
Common pitfalls
- Validating the hostname string instead of the resolved IP.
- Following redirects to internal addresses.
- Forgetting IPv6 and decimal/hex IP encodings.
Key points
- Allowlist when possible
- Validate resolved IPs and pin them
- Isolate the fetcher's network
Further reading: OWASP SSRF Prevention Cheat Sheet
Practice questions
What is DNS rebinding in the context of SSRF?
Answer
A hostname resolves to a safe IP during validation but to an internal IP when the actual connection is made; pinning the validated IP for the connection prevents it.
Why is cloud metadata a prime SSRF target?
Answer
Metadata endpoints (e.g. 169.254.169.254) can return temporary cloud credentials for the instance role; IMDSv2 and egress blocking reduce the risk.
Open in the app to mark lessons complete, take notes and track your progress.