What Is SSRF (Server-Side Request Forgery) and How to Prevent It

Retool's new app builder is where AI-generated code ships safely
Building apps with AI is easy. Getting them to production safely is another story.
SSRF attacks exploit a simple pattern: the application fetches a URL that the user specifies (to generate a preview, fetch an image, validate a webhook, or import data), and the attacker points that URL at something they should not be able to reach.
In traditional data centers, the target is usually internal services: a Redis instance, an internal admin panel, or a service mesh endpoint. In cloud environments, the target is almost always the instance metadata endpoint at 169.254.169.254, which returns the IAM role credentials attached to the EC2 instance. With those credentials, the attacker has AWS API access at whatever privilege level the role has.
How SSRF Works: The Attack Flow
Basic SSRF example:
An application has a feature that fetches a URL to generate a preview:
GET /preview?url=https://example.com/image.jpg
The server fetches the URL and returns the content. An attacker modifies the URL:
GET /preview?url=http://169.254.169.254/latest/meta-data/iam/security-credentials/
If the application fetches this without validation, it returns the names of IAM roles attached to the EC2 instance. The attacker then requests the credentials:
GET /preview?url=http://169.254.169.254/latest/meta-data/iam/security-credentials/MyAppRole
The response includes:
{
"AccessKeyId": "ASIAXXX",
"SecretAccessKey": "wJalr...",
"Token": "IQoJb3JpZ2luX2VjEA...",
"Expiration": "2026-06-26T18:00:00Z"
}
The attacker now has valid, temporary AWS credentials. They can use these to enumerate S3 buckets, access Secrets Manager, launch EC2 instances, or pivot to other AWS services: limited only by the IAM role's permissions.
Other common SSRF targets:
http://localhost:6379: Redis (no auth by default on many configurations)http://10.0.0.1/admin: internal admin panelshttp://169.254.169.254/latest/user-data: EC2 user data (often contains deployment scripts with secrets)http://100.100.100.200/latest/meta-data/: Alibaba Cloud metadatahttp://metadata.google.internal/computeMetadata/v1/: GCP metadatahttp://169.254.169.254/metadata/: Azure metadata
Blind SSRF: When the application does not return the response body to the attacker, the attack is blind. The attacker can still confirm internal network topology by observing timing differences (request took 200ms: that port is open) or DNS callbacks (the internal service resolved the attacker's DNS hostname).
Prevention Control 1: URL Allowlisting
The most effective SSRF prevention is to validate URLs against an allowlist of permitted destinations before fetching. If your application only needs to fetch images from specific CDNs, only permit those CDN domains.
Python example: allowlist-based URL validation:
from urllib.parse import urlparse
import ipaddress
import socket
ALLOWED_DOMAINS = {'images.example.com', 'cdn.trusted-vendor.com'}
def is_safe_url(url: str) -> bool:
try:
parsed = urlparse(url)
# Only allow https
if parsed.scheme not in ('https',):
return False
# Check domain against allowlist
if parsed.hostname not in ALLOWED_DOMAINS:
return False
# Resolve the hostname and check the IP
# (prevents DNS rebinding: where an allowed domain resolves to a private IP)
ip = socket.gethostbyname(parsed.hostname)
if is_private_ip(ip):
return False
return True
except Exception:
return False
def is_private_ip(ip: str) -> bool:
addr = ipaddress.ip_address(ip)
return addr.is_private or addr.is_loopback or addr.is_link_local or addr.is_reserved
# Usage
user_url = request.args.get('url')
if not is_safe_url(user_url):
abort(400, 'URL not permitted')
response = requests.get(user_url, timeout=5)
The DNS rebinding check is critical: An attacker can register a domain that initially resolves to a legitimate IP (passing your allowlist check) and then updates its DNS to point to 169.254.169.254 before the actual HTTP request. Resolve the hostname and check the resulting IP, not just the hostname.
Briefings like this, every morning before 9am.
Threat intel, active CVEs, and campaign alerts, distilled for practitioners. 50,000+ subscribers. No noise.
Prevention Control 2: Block Reserved IP Ranges
If your application must accept arbitrary URLs (webhook validators, link previewers, general URL importers), you cannot use a strict allowlist. Instead, block requests to private, loopback, link-local, and reserved IP ranges.
IP ranges to block for SSRF prevention:
REJECTED_NETWORKS = [
ipaddress.ip_network('10.0.0.0/8'), # RFC 1918 private
ipaddress.ip_network('172.16.0.0/12'), # RFC 1918 private
ipaddress.ip_network('192.168.0.0/16'), # RFC 1918 private
ipaddress.ip_network('127.0.0.0/8'), # Loopback
ipaddress.ip_network('169.254.0.0/16'), # Link-local (includes AWS metadata)
ipaddress.ip_network('::1/128'), # IPv6 loopback
ipaddress.ip_network('fc00::/7'), # IPv6 unique local
ipaddress.ip_network('fe80::/10'), # IPv6 link-local
ipaddress.ip_network('100.64.0.0/10'), # Shared address space (RFC 6598)
ipaddress.ip_network('0.0.0.0/8'), # 'This' network
ipaddress.ip_network('240.0.0.0/4'), # Reserved
]
def is_blocked_ip(ip_str: str) -> bool:
try:
addr = ipaddress.ip_address(ip_str)
return any(addr in network for network in REJECTED_NETWORKS)
except ValueError:
return True # Reject if unparseable
Also block non-standard ports if your application only needs to fetch web content:
ALLOWED_PORTS = {80, 443}
if parsed.port and parsed.port not in ALLOWED_PORTS:
return False # Block requests to Redis :6379, Elasticsearch :9200, etc.
Prevention Control 3: IMDSv2 Enforcement on EC2
Even with application-level SSRF prevention, enforce IMDSv2 at the infrastructure level. IMDSv2 requires a session token (obtained via a PUT request) before any metadata can be retrieved: a GET request to 169.254.169.254 returns an error, not credentials. SSRF attacks that use simple GET-based HTTP fetching cannot retrieve IMDSv2-protected metadata.
Enforce IMDSv2 on a running EC2 instance:
# Require IMDSv2 on an existing instance
aws ec2 modify-instance-metadata-options \
--instance-id i-1234567890abcdef0 \
--http-tokens required \
--http-endpoint enabled
# Verify the setting
aws ec2 describe-instances \
--instance-ids i-1234567890abcdef0 \
--query 'Reservations[].Instances[].MetadataOptions'
Enforce IMDSv2 for all new instances in an account (AWS Config + SCP):
// Service Control Policy to require IMDSv2 on all EC2 launches
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Deny",
"Action": "ec2:RunInstances",
"Resource": "arn:aws:ec2:*:*:instance/*",
"Condition": {
"StringNotEquals": {
"ec2:MetadataHttpTokens": "required"
}
}
}]
}
AWS Config rule to detect non-compliant instances:
aws configservice put-config-rule --config-rule '{
"ConfigRuleName": "ec2-imdsv2-check",
"Source": {"Owner": "AWS", "SourceIdentifier": "EC2_IMDSV2_CHECK"}
}'
IMDSv2 is not a complete SSRF fix: it only prevents SSRF-based metadata credential theft. SSRF can still reach other internal services. All three controls (allowlisting, IP blocking, IMDSv2) should be applied together.
The bottom line
SSRF allows attackers to make HTTP requests from your server to internal targets: with the AWS EC2 metadata endpoint at 169.254.169.254 being the most dangerous target because it returns IAM credentials. Three controls prevent SSRF: allowlist-based URL validation (only permit expected domains, resolve hostnames and check resulting IPs), block all requests to private/loopback/link-local IP ranges at the application level, and enforce IMDSv2 on all EC2 instances to protect the metadata endpoint even if application-level controls fail.
Frequently asked questions
What is a server-side request forgery (SSRF) attack?
SSRF tricks a web application into making HTTP requests to a destination the attacker specifies: typically an internal service, localhost, or cloud instance metadata endpoint that the attacker cannot reach directly from the internet. In cloud environments, the most common SSRF target is the EC2 metadata endpoint at 169.254.169.254, which returns IAM role credentials that the attacker can use for AWS API access.
How do you prevent SSRF attacks?
Three controls in combination: validate URLs against an allowlist of permitted domains (and resolve hostnames to check the resulting IP against private ranges to prevent DNS rebinding), block all requests to private/loopback/link-local IP ranges including 169.254.0.0/16 at the application level, and enforce IMDSv2 on EC2 instances to require a session token before metadata can be accessed.
What is the AWS EC2 metadata service and why is it an SSRF target?
The EC2 metadata service is an HTTP endpoint at 169.254.169.254 that provides instance configuration data including IAM role credentials. Any application running on the EC2 instance can query it without authentication (under IMDSv1). An SSRF vulnerability allows an attacker to make the server fetch this URL on their behalf, retrieving the IAM credentials and gaining the instance role's permissions in AWS. Enforce IMDSv2, which requires a session token obtained via a PUT request: a standard HTTP redirect in SSRF cannot obtain the token, blocking the credential theft. Enforce IMDSv2 via the AWS Console or: 'aws ec2 modify-instance-metadata-options --instance-id i-xxx --http-tokens required'.
How do I test my application for SSRF vulnerabilities?
SSRF testing requires sending URL parameters to your application pointing at internal resources and observing whether responses contain internal data. Test vectors: submit requests with the URL parameter set to http://169.254.169.254/latest/meta-data/ (AWS metadata), http://localhost:80, http://internal-hostname, and http://192.168.1.1 — if the response contains internal data, the application is vulnerable. Use Burp Suite's Collaborator to detect blind SSRF: if the server makes a DNS lookup or HTTP request to the Collaborator URL, SSRF is confirmed even if the response is not reflected. Include SSRF testing in your web application penetration test scope explicitly, as it is often missed in automated scanner testing.
What is blind SSRF and how is it different from regular SSRF?
Regular SSRF returns the fetched content in the HTTP response, making internal data directly visible to the attacker. Blind SSRF triggers an internal request but does not reflect the response: the attacker can confirm the server is making requests (via out-of-band DNS or HTTP beaconing) but cannot read the response directly. Blind SSRF is harder to exploit for data exfiltration but still dangerous: it can be used for port scanning internal networks (by timing responses to detect open vs closed ports) and for hitting internal services that perform actions on receiving an HTTP request (webhooks, metadata modification endpoints). Blind SSRF requires out-of-band detection methods like Burp Collaborator.
How do DNS rebinding attacks bypass SSRF protections and how do you prevent them?
DNS rebinding is an attack technique that defeats hostname-based SSRF allowlists. The attacker registers a domain (attacker.com) with a very short TTL and initially configures it to resolve to a legitimate public IP that passes your allowlist check. After your application validates the hostname but before it makes the actual HTTP request, the attacker updates the DNS record to resolve to an internal IP (such as 169.254.169.254). The application fetches the cached-then-expired DNS record and receives the internal address, bypassing the hostname validation. Prevention requires resolving the hostname to an IP address and checking that IP against your blocked ranges before making the request, not just validating the hostname string. After resolving, compare the resulting IP against all private, loopback, and link-local ranges. Critically, make the DNS lookup and the HTTP connection in a single operation or pin the IP after the check: some HTTP libraries re-resolve the hostname for each connection, which reintroduces the rebinding window. Alternatively, use a dedicated SSRF-safe HTTP fetching library that performs these checks internally rather than building the validation manually.
How do you test for SSRF vulnerabilities using Burp Suite and ffuf?
Burp Suite SSRF testing workflow: (1) Identify all parameters that accept URLs, hostnames, or IP addresses -- including less obvious fields like webhook URLs, import-from-URL features, PDF generators, and image-fetch endpoints. (2) Replace each URL value with http://169.254.169.254/latest/meta-data/ and observe responses; if the response contains AWS metadata, SSRF is confirmed. (3) Enable Burp Collaborator and submit your Collaborator URL as the parameter value -- any out-of-band DNS lookup or HTTP request to Collaborator confirms blind SSRF. (4) Test filter bypasses: decimal IP notation (2852039166 for 169.254.169.254), hex notation (0xa9fea9fe), IPv6 representation (::ffff:169.254.169.254), and URL encoding. ffuf-based SSRF fuzzing: use ffuf to sweep a parameter across a wordlist of internal IP ranges and ports: 'ffuf -u https://target.com/fetch?url=FUZZ -w internal-ssrf-payloads.txt -mc 200' where the wordlist contains http://10.0.0.1:6379 (Redis), http://10.0.0.1:9200 (Elasticsearch), http://172.16.0.1:8080 (internal app). Response-size differences indicate open ports even without response body reflection. SSRF is frequently missed in automated scanner output -- always include it explicitly in manual testing scope for applications with URL-fetching features.
Sources & references
Free resources
Critical CVE Reference Card 2025–2026
25 actively exploited vulnerabilities with CVSS scores, exploit status, and patch availability. Print it, pin it, share it with your SOC team.
Ransomware Incident Response Playbook
Step-by-step 24-hour IR checklist covering detection, containment, eradication, and recovery. Built for SOC teams, IR leads, and CISOs.
Get threat intel before your inbox does.
50,000+ security professionals read Decryption Digest for early warnings on zero-days, ransomware, and nation-state campaigns. Free, daily, no spam.
Unsubscribe anytime. We never sell your data.

Founder & Cybersecurity Evangelist, Decryption Digest
Cybersecurity professional with expertise in threat intelligence, vulnerability research, and enterprise security. Covers zero-days, ransomware, and nation-state operations for 50,000+ security professionals every morning.
Win a $2,495 Black Hat pass.
Full-access to Black Hat USA 2026 in Las Vegas. Subscribe free to enter.
