Server-Side Request Forgery (SSRF) is a critical web vulnerability where an attacker tricks your server into making HTTP requests to unintended destinations — internal services, admin panels, or cloud metadata endpoints that should never be reachable from the internet. It sits in the OWASP Top 10 because a single unvalidated URL parameter can hand an attacker the keys to your entire AWS or GCP environment. For Indian development teams deploying on cloud infrastructure, SSRF is not theoretical — it is the exact attack class behind some of the largest cloud breaches ever documented.
What Is SSRF and Why Does It Happen
When your backend fetches a URL on behalf of a user — a webhook validator, a link preview generator, a PDF renderer, an image import feature, an API proxy — and that URL comes from user input without strict validation, you have a potential SSRF surface.
The server makes the request using its own network identity and IAM role. From the internal network's perspective, the request arrives from a trusted internal IP. Firewalls that block external attackers do nothing here — the attacker is riding inside your own server.
Consider a typical vulnerable pattern:
# VULNERABLE — never do this
import requests
def fetch_preview(url):
response = requests.get(url) # url comes directly from user input
return response.textThe attacker submits url=http://169.254.169.254/latest/meta-data/iam/security-credentials/ and your server dutifully fetches the AWS EC2 instance metadata, returning live IAM credentials.
The SSRF Attack Flow
graph TD
A[Attacker submits crafted URL] --> B[App server makes HTTP request]
B --> C{Target resolved}
C --> D[Internal service
192.168.x.x / localhost]
C --> E[Cloud metadata
169.254.169.254]
C --> F[Other cloud tenants
10.x.x.x]
D --> G[Admin panel / DB port / Redis]
E --> H[IAM credentials / token]
F --> I[Cross-tenant data access]
G --> J[Lateral movement]
H --> J
I --> J
J --> K[Full cloud account takeover]
style A fill:#5f1e1e,stroke:#EF4444,color:#e2e8f0
style B fill:#5f1e1e,stroke:#EF4444,color:#e2e8f0
style C fill:#1e3a5f,stroke:#3B82F6,color:#e2e8f0
style D fill:#5f1e1e,stroke:#EF4444,color:#e2e8f0
style E fill:#5f1e1e,stroke:#EF4444,color:#e2e8f0
style F fill:#5f1e1e,stroke:#EF4444,color:#e2e8f0
style G fill:#5f1e1e,stroke:#EF4444,color:#e2e8f0
style H fill:#5f1e1e,stroke:#EF4444,color:#e2e8f0
style I fill:#5f1e1e,stroke:#EF4444,color:#e2e8f0
style J fill:#5f1e1e,stroke:#EF4444,color:#e2e8f0
style K fill:#5f1e1e,stroke:#EF4444,color:#e2e8f0Basic SSRF vs Blind SSRF
Not all SSRF looks the same. Understanding the two variants determines how attackers probe and how you detect them.
Basic (in-band) SSRF returns the response directly to the attacker. The vulnerability in the Python snippet above is basic SSRF — the attacker can read the full response body, including AWS credentials, internal API payloads, or database query results.
Blind (out-of-band) SSRF does not return the response to the attacker's browser. Instead, the attacker infers success through:
- DNS lookups — they point the URL at a domain they control (e.g., via Burp Collaborator or interactsh) and detect whether the server resolves it
- Timing differences — a request to
http://169.254.169.254/that times out vs. one to a real host that responds in 50ms - Secondary side effects — a request that triggers an internal email, a webhook, or a log entry
169.254.169.254 address is the AWS/GCP/Azure link-local metadata endpoint. Every EC2, GCE, and Azure VM can reach it. If your app server can fetch arbitrary URLs, an attacker can exfiltrate live IAM tokens, SSH keys, and instance identity documents — no authentication required.Know your vulnerabilities before attackers do
Run a free VAPT scan — takes 5 minutes, no signup required.
Book Your Free ScanThe Capital One Breach Pattern
In 2019, a misconfigured WAF allowed an attacker to exploit an SSRF vulnerability to query the AWS metadata endpoint (169.254.169.254). The returned IAM credentials had overly broad S3 permissions. The attacker exfiltrated over 100 million customer records from S3 buckets.
The core pattern repeats across cloud breaches:
- SSRF reaches the metadata endpoint
- Metadata returns temporary IAM credentials (
AccessKeyId,SecretAccessKey,Token) - Attacker calls AWS APIs directly from their machine using those credentials
- Broad IAM permissions allow S3 ListBuckets → GetObject → mass data exfiltration
SSRF Targets Beyond Cloud Metadata
Metadata endpoints get the headlines, but SSRF gives attackers access to anything reachable from your server's network:
| Target | Example endpoint | What attackers gain |
|---|---|---|
| Cloud metadata (AWS) | http://169.254.169.254/latest/meta-data/ | IAM credentials, instance role, SSH keys |
| Cloud metadata (GCP) | http://metadata.google.internal/computeMetadata/v1/ | Service account tokens |
| Internal admin panels | http://localhost:8080/admin | App admin access without auth |
| Internal databases | http://localhost:6379 (Redis) | Cache data, session tokens |
| Internal microservices | http://10.0.1.45:8001/internal-api | Service-to-service APIs bypassing auth |
| IMDSv2 token endpoint | http://169.254.169.254/latest/api/token | Session token required for IMDSv2 credential fetch |
| CI/CD agents | http://localhost:8090/ | Build secrets, deploy keys |
169.254.0.0/16), loopback (127.0.0.1, ::1), and RFC-1918 private ranges (10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16) must all be blocked at the egress validation layer. Attackers use decimal encoding, IPv6 notation, and DNS aliases to bypass naive blocklists — an allowlist is safer than a denylist.Prevention Layers for Indian Dev Teams
The SSRF prevention stack is not a single control. It is defence in depth across multiple layers. Here is the full control set ranked by effectiveness:
pie title SSRF Defence Layer Distribution
"Egress Allowlist" : 30
"IMDSv2 Enforcement" : 20
"Network Segmentation" : 20
"URL Validation and Re-resolve" : 15
"WAF SSRF Rules" : 10
"Logging and Alerting" : 51. Enforce IMDSv2 on Every EC2 Instance
AWS Instance Metadata Service v2 (IMDSv2) requires a PUT request with a TTL header before issuing a session token. A simple GET to 169.254.169.254 returns nothing. Enable it at launch or via instance modification:
# Enforce IMDSv2 on a running instance
aws ec2 modify-instance-metadata-options \
--instance-id i-0abc123 \
--http-tokens required \
--http-put-response-hop-limit 1Set --http-put-response-hop-limit 1 so the token cannot be relayed through a container boundary. This single control eliminates the Capital One attack class on AWS.
2. Validate and Re-resolve URLs After DNS Resolution
A blocklist on the URL string is not sufficient. Attackers use DNS rebinding (the domain resolves to a public IP for the first lookup, then rebinds to 169.254.169.254 for the server's actual fetch). The correct pattern: resolve the hostname to an IP, validate the IP against your blocklist, then make the HTTP request to that resolved IP — not the original hostname.
# SECURE pattern — resolve first, validate IP, then fetch
import socket
import ipaddress
import requests
BLOCKED_NETWORKS = [
ipaddress.ip_network("169.254.0.0/16"), # link-local / metadata
ipaddress.ip_network("127.0.0.0/8"), # loopback
ipaddress.ip_network("10.0.0.0/8"), # RFC-1918
ipaddress.ip_network("172.16.0.0/12"), # RFC-1918
ipaddress.ip_network("192.168.0.0/16"), # RFC-1918
ipaddress.ip_network("::1/128"), # IPv6 loopback
ipaddress.ip_network("fe80::/10"), # IPv6 link-local
]
def is_safe_url(url: str) -> bool:
from urllib.parse import urlparse
hostname = urlparse(url).hostname
try:
resolved_ip = socket.gethostbyname(hostname)
ip_obj = ipaddress.ip_address(resolved_ip)
for net in BLOCKED_NETWORKS:
if ip_obj in net:
return False
return True
except Exception:
return False
def safe_fetch(url: str):
if not is_safe_url(url):
raise ValueError("URL resolves to a blocked network")
# Make the actual request to the resolved IP to prevent rebinding
response = requests.get(url, timeout=5, allow_redirects=False)
return response.text3. Use an Egress Allowlist, Not a Denylist
If your webhook or integration feature only needs to reach a known set of external endpoints, restrict outbound connections to those endpoints only. Block all other egress at the security group or firewall level. This is the strongest control — an attacker cannot reach 169.254.169.254 if your server's egress rules prohibit all traffic except to your allowlisted destinations.
0.0.0.0/0 outbound security group rule from application servers that fetch user-supplied URLs. Replace it with explicit allow rules for your dependencies (payment gateways, email providers, storage endpoints). Treat outbound traffic with the same zero-trust posture as inbound.4. Network Segmentation
Servers that handle user-supplied URL fetching should sit in a dedicated subnet with no direct access to your internal microservice network. A compromised server in an isolated subnet limits lateral movement even if SSRF is exploited. Use VPC security groups, network ACLs, and service mesh policies to enforce this boundary.
5. Disable Redirects and Enforce Protocol Allowlists
HTTP redirects can chain a valid-looking URL to an internal target. Set allow_redirects=False (or equivalent) on your HTTP client and handle redirects manually with re-validation at each hop. Also restrict accepted schemes to https:// only — file://, gopher://, dict://, and ftp:// are classic SSRF bypass vectors.
SSRF in Indian Regulatory Context
CERT-In's Cyber Security Guidelines require organisations to report significant breaches within six hours. An SSRF-enabled cloud credential theft that results in data exfiltration qualifies. India's DPDP Act 2023 mandates notification to the Data Protection Board when personal data is compromised — the breach affects regulatory standing regardless of whether the SSRF was "just a misconfiguration."
Bachao.AI's automated VAPT scanner, built by Dhisattva AI Pvt Ltd, includes SSRF detection as part of its active scan suite — testing for open redirect chains, metadata endpoint reachability, and DNS rebinding vectors on every scan.
SSRF Detection Checklist
Before shipping any feature that fetches external URLs, walk through this checklist:
| Control | Implemented | Notes |
|---|---|---|
| IMDSv2 enforced on all EC2 instances | AWS Console → EC2 → Actions → Modify IMDS options | |
| Outbound security group restricts egress | No 0.0.0.0/0 outbound on app tier | |
| URL validation resolves DNS before fetch | Must re-validate IP post-resolve | |
| Blocked networks include link-local + RFC-1918 | 169.254.0.0/16, 10/8, 172.16/12, 192.168/16, loopback | |
HTTP client has allow_redirects=False | Validate each redirect hop | |
Only https:// scheme accepted | Block file://, gopher://, dict://, ftp:// | |
| Timeout set on all outbound requests | Prevents slow-loris style probing | |
| Unexpected internal requests trigger alert | SIEM rule on internal IP in outbound request log |