Back to Cheat Sheets

🌐 Server-Side Request Forgery (SSRF)

OWASP Web Top 10 - #10

MEDIUM RISK

📋 What Is It?

Server-Side Request Forgery (SSRF) occurs when an attacker can trick a server into making unintended requests to internal services, cloud metadata endpoints, or external systems. This bypasses firewalls and exposes internal infrastructure.

#10 OWASP Rank
2.72% Incidence Rate
9.1 Avg CVE Score

⚠️ Common Exploits

  • Internal Service Access: Accessing localhost:8080/admin
  • Cloud Metadata: Reading AWS credentials at 169.254.169.254
  • Port Scanning: Mapping internal network infrastructure
  • Firewall Bypass: Accessing services behind firewall
  • File Reading: Using file:// protocol to read local files
  • DNS Rebinding: Bypassing URL filters via DNS tricks

🔴 Attack Flow

1. App accepts URL parameter for fetching

2. Attacker provides: http://169.254.169.254

3. Server fetches AWS metadata endpoint

4. Returns IAM credentials to attacker

5. BREACH: Full AWS account access!

❌ Vulnerable Code

// Bad: No URL validation - accepts any URL import requests @app.route('/fetch', methods=['POST']) def fetch_url(): url = request.json.get('url') # Vulnerable! Can access internal services response = requests.get(url) return response.text // Examples of malicious URLs: # http://localhost:8080/admin # http://169.254.169.254/latest/meta-data/iam/ # http://192.168.1.1/admin # file:///etc/passwd // Bad: No protocol restriction @app.route('/proxy') def proxy(): target = request.args.get('url') # Allows file://, gopher://, dict://, etc. return urllib.request.urlopen(target).read()

✅ Secure Code

// Good: Whitelist allowed domains import requests from urllib.parse import urlparse ALLOWED_DOMAINS = ['example.com', 'api.example.com'] BLOCKED_IPS = [ '127.0.0.1', 'localhost', # Localhost '169.254.169.254', # AWS metadata '10.', '172.16.', '192.168.' # Private ranges ] @app.route('/fetch', methods=['POST']) def fetch_url(): url = request.json.get('url') # Validate URL scheme if not url.startswith(('http://', 'https://')): return error("Invalid protocol"), 403 # Parse and validate domain parsed = urlparse(url) if not any(domain in parsed.netloc for domain in ALLOWED_DOMAINS): return error("Domain not whitelisted"), 403 # Block internal IPs for blocked in BLOCKED_IPS: if blocked in url: return error("Access denied"), 403 # Fetch with timeout try: response = requests.get(url, timeout=5) return response.text except: return error("Fetch failed"), 500 // Good: Additional DNS validation import socket def is_safe_host(hostname): try: ip = socket.gethostbyname(hostname) # Check if IP is private/internal if ip.startswith(('10.', '172.', '192.168.', '127.')): return False return True except: return False

✓ Prevention Checklist

  • Whitelist allowed domains/URLs only
  • Block internal IP ranges (10.x, 192.168.x, 127.x)
  • Block cloud metadata IPs (169.254.169.254)
  • Allow only http:// and https:// protocols
  • Disable redirects or validate redirect targets
  • Use network segmentation
  • Implement DNS validation
  • Disable IMDSv1 on cloud platforms
  • Use timeout for external requests
  • Monitor outbound connections

🔍 Detection & Tools

Testing Tools:

Burp Suite SSRFmap OWASP ZAP Interactsh

Prevention Libraries:

SafeCurl ssrf_filter urllib validators requests-guard

How to Test:

  • Try localhost URLs (127.0.0.1, localhost)
  • Test cloud metadata (169.254.169.254)
  • Attempt internal IP ranges (10.x, 192.168.x)
  • Test file:// protocol

🌍 Real-World Breaches

  • Capital One (2019): SSRF on AWS EC2 metadata service led to 100M+ records stolen
  • Uber (2016): SSRF used to access internal Uber systems
  • Shopify (2017): SSRF allowed reading internal files and metadata
  • HackerOne (2018): SSRF on internal services led to data exposure

📌 Quick Tips

  • DO NOT accept arbitrary URLs
  • DO NOT allow file:// protocol
  • DO whitelist allowed domains
  • DO block internal IP ranges
  • DO disable IMDSv1 on AWS

📜 Compliance

Related Standards:

  • CWE CWE-918
  • NIST 800-53 SC-7
  • OWASP ASVS V5.2.6
  • PCI-DSS Requirement 6.5.1