SSRF Protection Patterns
Server-side request forgery. The defenses.
Validate URLs
Server-Side Request Forgery (SSRF) is the vulnerability where an attacker tricks a server into making HTTP requests to destinations the attacker chose, including internal services that are not normally reachable from outside. SSRF was the vector for the Capital One breach and many others; it is the bug that "pull this URL for me" features keep introducing. The defense is layered, starting with code-level URL validation.
What URL validation actually requires:
- Allowlist destinations.: When the application makes outbound HTTP requests on behalf of users, the destination must be on an allowlist. The allowlist is hardcoded or in source-controlled configuration; user input cannot extend it. Anything not on the list is rejected before the request is made.
- Reject internal IPs.: The allowlist excludes internal IP ranges: 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, 169.254.0.0/16, 127.0.0.0/8, fc00::/7, fe80::/10, ::1. Even if a user-supplied URL would have been allowed, requests that resolve to internal IPs are blocked. Resolve before fetch; check the resolved IP, not just the hostname.
- Code-level enforcement.: The validation happens in the request-making code, not in a wrapper that the developer might forget to use. The HTTP client library itself rejects requests to internal IPs by default, and the application has to explicitly allow them on a per-call basis.
- Reject ambiguous schemes.: Block file:// (lets the attacker read local files), gopher:// (lets them craft arbitrary TCP payloads), data:// (lets them inject inline content), dict:// (similar). Allow only http:// and https://, and disable redirects to disallowed schemes.
- DNS rebinding awareness.: An attacker can register a domain whose DNS first resolves to a public IP (passes allowlist) and then resolves to an internal IP on the second lookup (the actual fetch). Defense: resolve once, use the resolved IP throughout. Or pin to the resolved IP for the duration of the request.
Code-level validation is the first line of defense and the cheapest. Most SSRF bugs would have been caught by a simple "is this URL on the allowlist" check at the right layer.
Network
Code-level validation is necessary but not sufficient. Code has bugs. The attacker has unlimited time to find them. The second layer of defense is network-level: even if the application makes a request to an internal IP, the network refuses to deliver it.
- Egress firewall blocks internal access.: The application's network egress is restricted by firewall rules. Outbound requests to internal IP ranges are blocked at the network layer. Even if the application code makes the request, the packet never reaches the destination.
- Default-deny egress in Kubernetes.: NetworkPolicy with egress rules denies all outbound traffic except to explicitly allowed destinations. The application can reach the third-party APIs it needs and the internal services it depends on; everything else is dropped at the cluster network level.
- Defense in depth.: Code validation and network filtering are independent. A bug in one does not bypass the other. The combination is what makes SSRF protection robust against the inevitable code-level mistakes.
- Egress proxy as enforcement point.: Some teams route all outbound HTTP through an egress proxy that enforces the allowlist independently of the application. The proxy logs every request, blocks disallowed destinations, and produces an audit trail of what the application is reaching.
- Application-specific subnets.: Customer-input-handling applications run in subnets with restrictive egress rules. Internal services run in different subnets with more permissive rules. The network architecture reflects which workloads are exposed to user input and which are not.
Network-level enforcement catches the SSRF bugs that code-level validation missed. The two layers compound; the security posture is the product of both, not the sum.
IMDSv2
Cloud instance metadata services are the most common SSRF target on AWS. The metadata endpoint at 169.254.169.254 returns instance credentials, which an attacker can use to escalate from "I made you fetch a URL" to "I have your AWS credentials." The defense is IMDSv2, which fundamentally changes how the metadata service is accessed.
- Block instance metadata via SSRF.: IMDSv2 requires a session token obtained via PUT request before any GET can return data. SSRF, which usually only sends GET requests, cannot get the token, so it cannot read the metadata. The Capital One breach would not have been possible against IMDSv2.
- Specific AWS protection.: IMDSv2 is AWS-specific (GCP and Azure have similar mechanisms with their own names). Enable it on every EC2 instance, in every Auto Scaling Group launch template, on every container task definition. Disable IMDSv1 entirely once the migration is complete.
- Token requires PUT, hop limit 1.: The token request uses HTTP PUT, which most SSRF vulnerabilities cannot send. Even if the attacker could send PUT, the token has a hop limit of 1, meaning the response cannot traverse a proxy or another instance. The defense is multi-layered.
- Audit current state.: Run a fleet-wide audit to find instances still allowing IMDSv1. The aws-sdk includes a check; the IMDSv2-only mode setting on each instance should be on. Any instance with IMDSv1 still allowed is a residual SSRF risk; migrate them.
- Defense across cloud and on-prem.: The same pattern applies on GCP (use the metadata server v1 with metadata-flavor header), Azure (require Metadata header), and on-prem (block 169.254.0.0/16 entirely if you have no metadata service). The lesson generalizes; the implementation differs per cloud.
SSRF protection requires layered defenses: code-level URL validation, network-level egress filtering, and platform-specific protections like IMDSv2. Nova AI Ops audits the configuration of each layer, surfaces the cases where IMDSv1 is still allowed, and tracks egress traffic patterns to flag the application behavior that looks like SSRF in flight.