Server-Side Request Forgery was classified as a "notable mention" in the OWASP Top 10 2021. By 2025, it earned a standalone category. The reason: SSRF combined with cloud metadata services has become one of the most reliable paths to full infrastructure compromise.
What SSRF Is and Why It's Dangerous
SSRF occurs when an application accepts a URL from user input and makes a server-side HTTP request to that URL. The vulnerability is in the trust model: the request comes from the server, not the user's browser.
This matters because the server may have access to:
- Cloud provider metadata endpoints (169.254.169.254 — AWS, GCP, Azure)
- Internal services not exposed to the internet (databases, admin panels, microservices)
- Cloud credentials via IMDS (Instance Metadata Service)
The canonical SSRF-to-credentials attack:
1. Application feature: "Import from URL" or "Preview URL"
2. User submits: http://169.254.169.254/latest/meta-data/iam/security-credentials/
3. Server fetches this URL (server has access to IMDS, browser doesn't)
4. Response includes: AccessKeyId, SecretAccessKey, Token
5. Attacker has AWS API access with the instance's IAM role
Real-world breaches attributed to SSRF include the 2019 Capital One breach (100M+ records), multiple AWS S3 data exposures, and various cloud-hosted application compromises.
Attack Surface — Where SSRF Hides
SSRF lives anywhere an application makes outbound HTTP requests based on user-controlled input:
Direct URL Parameters
/api/preview?url=https://external.com/image.jpg
/api/check-status?endpoint=https://service.internal
/api/import?source=https://...
Form-Based File Import
Upload from URL: [https://...]
Fetch RSS feed: [https://...]
Import from webhook: [https://...]
JSON/API Body Parameters
{ "callback_url": "https://attacker.com/collect" }
{ "image_url": "http://169.254.169.254/..." }
{ "webhook": "https://..." }
Headers and Indirect Sources
Less common but present: Referer header processing, X-Forwarded-For in certain configurations, SVG image processing (SVGs can contain HTTP references).
Cloud Metadata Service Exploitation
Every major cloud provider has an Instance Metadata Service (IMDS) accessible from within the VM:
AWS (IMDSv1):
http://169.254.169.254/latest/meta-data/
http://169.254.169.254/latest/meta-data/iam/security-credentials/{role-name}
IMDSv1 is stateless — any SSRF that reaches this endpoint returns credentials. IMDSv2 requires a PUT request with a session token first, making blind SSRF exploitation harder (but not impossible).
GCP:
http://metadata.google.internal/computeMetadata/v1/
http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token
Azure:
http://169.254.169.254/metadata/instance?api-version=2021-02-01
http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource=...
The response from these endpoints includes temporary IAM credentials with the permissions of the instance's IAM role. In poorly configured environments, instances have broad permissions — sometimes AdministratorAccess.
Detection: Active SSRF Probing
Passive analysis (reviewing source code for URL-accepting parameters) catches some SSRF. Active probing catches all of it.
Standard Active Probe Pattern:
- Set up an out-of-band (OOB) listener — a server controlled by you that logs incoming requests
- Inject OOB listener URL into every URL-accepting parameter
- Observe if the application's server makes a request to your OOB listener
If your OOB listener receives a request, SSRF is confirmed. The originating IP will be the application server, not the user's browser — this distinguishes SSRF from SSRF-like client-side request forgery.
Blind SSRF Detection:
Many SSRF vulnerabilities are "blind" — the application doesn't return the fetched URL's content to the user (used for internal webhooks, import jobs, etc.). OOB listeners are the only reliable detection method for blind SSRF.
Payload Variants:
http://169.254.169.254/latest/meta-data/ (AWS IMDS)
http://[::]:80/ (IPv6 localhost)
http://0.0.0.0/ (alternate localhost form)
http://127.0.0.1:8080/admin (internal admin panel)
file:///etc/passwd (local file via file:// protocol)
dict://localhost:11211/stats (memcached via dict:// protocol)
gopher://localhost:6379/_INFO (Redis via gopher://)
Protocol variants (file://, dict://, gopher://) exploit SSRF beyond HTTP — some applications support multiple URL schemes without restriction.
Prevention
1. IMDSv2 (AWS) — Require Token-Based Metadata Access
Enforce IMDSv2 at instance launch:
aws ec2 modify-instance-metadata-options \
--instance-id i-1234567890abcdef0 \
--http-tokens required \
--http-endpoint enabled
Or via Terraform:
resource "aws_instance" "app" {
metadata_options {
http_tokens = "required" # enforces IMDSv2
http_endpoint = "enabled"
}
}
IMDSv2 doesn't eliminate SSRF — it makes credential theft via SSRF harder because the exploiting request needs to first make a PUT to get a session token. It's defense-in-depth, not a fix.
2. URL Allowlisting
If your application must fetch URLs (image import, webhook validation, URL preview), implement strict allowlisting:
import ipaddress, socket
from urllib.parse import urlparse
ALLOWED_SCHEMES = {'https'}
BLOCKED_NETWORKS = [
ipaddress.ip_network('10.0.0.0/8'),
ipaddress.ip_network('172.16.0.0/12'),
ipaddress.ip_network('192.168.0.0/16'),
ipaddress.ip_network('169.254.0.0/16'), # IMDS
ipaddress.ip_network('127.0.0.0/8'), # loopback
ipaddress.ip_network('::1/128'), # IPv6 loopback
ipaddress.ip_network('fd00::/8'), # IPv6 private
]
def is_safe_url(url: str) -> bool:
parsed = urlparse(url)
if parsed.scheme not in ALLOWED_SCHEMES:
return False
# Resolve hostname to IP — check against blocklist AFTER resolution
# DNS rebinding bypass: resolve once, block private ranges
try:
ip = ipaddress.ip_address(socket.gethostbyname(parsed.hostname))
except (socket.gaierror, ValueError):
return False
return not any(ip in network for network in BLOCKED_NETWORKS)
The key: resolve DNS before comparing to blocklists. DNS rebinding attacks use a hostname that initially resolves to a public IP (passing the check) then rapidly changes to resolve to a private IP (reaching internal services). Resolve once, cache the result, use it for the request.
3. Network Egress Restrictions
At the infrastructure level, restrict outbound HTTP from application servers to a firewall allowlist. Application servers rarely need to make arbitrary outbound requests — if they do, route through a forward proxy that applies its own allowlist.
Application server → egress only to:
- cdn.yourapp.com (your CDN)
- api.stripe.com (payment processing)
- api.sendgrid.com (email)
- NOT: 169.254.169.254
- NOT: internal subnets
PentestCheck injects SSRF probes (including OOB detection) into every URL-accepting parameter during DAST scans. SSRF findings are categorized as CRITICAL when cloud metadata access is confirmed.