Stop SSRF in Its Tracks: A Hardened Guide to API & Web Security

Security tutorial - IT technology blog
Security tutorial - IT technology blog

The SSRF Threat: When Your Own Server Turns Against You

Server-Side Request Forgery (SSRF) is the ultimate “inside job.” While a standard XSS attack targets your users, an SSRF exploit tricks your own backend into becoming the attacker. It forces your server to fetch data from internal resources—like your Redis cache or a cloud metadata service—that were never meant to see the light of day.

The 3 AM Wake-up Call: Security by Default

I learned the value of “Security by Default” the hard way—staring at a terminal full of SSH brute-force logs at 3:00 AM. That experience cemented a vital rule: hackers don’t just look for open windows; they look for ways to trick your system into opening the door from the inside. SSRF is exactly that trick. If your app handles user-provided URLs—whether for profile pictures, webhook integrations, or PDF generators—you are in the crosshairs.

How SSRF Actually Happens

Feature requests often require fetching external data. For instance, a user might provide a link to a remote image. Your server then fires off a GET request to that URL.

However, an attacker might input http://127.0.0.1:6379 instead. Suddenly, your server is probing your internal Redis instance. In cloud environments like AWS, an attacker could target http://169.254.169.254/latest/meta-data/ to hijack IAM credentials. We can never trust a request destination initiated by our own backend if the input originates from a user.

Layering Your Defenses: Beyond Regex

Don’t rely on simple string matching. Prevention requires building layers of isolation at both the network and application levels.

Enforcing Network Isolation

My first move in any production setup is locking down egress traffic. Your web server rarely needs to talk to every internal IP. By using iptables or cloud Security Groups, you can physically prevent the web server from reaching the management network. In a recent audit, we found that simply blocking the 10.0.0.0/8 range at the firewall level neutralized 90% of internal scanning attempts.

# Block the www-data user from accessing the AWS Metadata service
iptables -A OUTPUT -m owner --uid-owner www-data -d 169.254.169.254 -j REJECT

Hardening HTTP Clients

Standard libraries follow redirects by default. This is a massive trap. An attacker can provide a safe-looking URL that redirects to a restricted internal IP (like 192.168.1.1). Use libraries that let you hook into the request lifecycle. You must validate the IP address after DNS resolution but before the request is sent to prevent DNS rebinding attacks.

The “Deny-By-Default” Implementation

Blocklists for localhost or 127.0.0.1 are easily bypassed with decimal or hexadecimal encoding. The only reliable approach is an allowlist.

Validating IPs in Python

Here is how I handle URL fetching. This script resolves the domain and checks the IP against reserved ranges before any data is exchanged.

import socket
import ipaddress
import requests

def is_safe_url(url):
    try:
        # 1. Extract hostname and resolve DNS
        host = url.split('/')[2].split(':')[0]
        ip_address = socket.gethostbyname(host)
        ip = ipaddress.ip_address(ip_address)
        
        # 2. Kill requests to private or loopback ranges
        if ip.is_private or ip.is_loopback or ip.is_link_local:
            return False
            
        return True
    except Exception:
        return False

def fetch_user_content(url):
    if not is_safe_url(url):
        raise ValueError("Access Denied: Insecure destination detected.")
    
    # Disable redirects to block 302-based bypasses
    return requests.get(url, allow_redirects=False, timeout=5)

Upgrading to AWS IMDSv2

If you’re on EC2, move to IMDSv2 immediately. Unlike the older version, IMDSv2 requires a session token via a PUT request. Because most SSRF bugs only allow GET requests, this single change makes it nearly impossible for an attacker to steal your instance metadata.

# Require tokens for metadata access on a specific instance
aws ec2 modify-instance-metadata-options \
    --instance-id i-0abcdef1234567890 \
    --http-tokens required \
    --http-endpoint enabled

Continuous Verification: Testing Your Guardrails

Security isn’t a “set and forget” task. You need to prove your defenses work.

Simulated Attacks

Fire up Burp Suite or OWASP ZAP. Try to fetch http://localhost:80 or probe for internal services like Elasticsearch on port 9200. If your app returns anything other than a generic error, your logic has a leak. I also use interact.sh to monitor if my server is making unauthorized outbound calls during these tests.

Monitoring Egress Traffic

Logs are your eyes and ears. Monitor your outbound requests for calls made directly to IP addresses instead of hostnames. High volumes of 403 Forbidden responses from internal ranges are a huge red flag—they often signal an attacker performing an automated port scan. Tools like Falco can even alert you the moment a web process tries to touch the metadata service or a sensitive database port.

Treat user input as toxic. By combining network-level walls, strict code validation, and constant monitoring, you turn a critical SSRF risk into a non-issue. Start with the network, harden the code, and never stop testing.

Share: