How to Fix an SSRF Vulnerability in a URL Fetcher

An SSRF vulnerability in a URL fetcher means your server will fetch any URL the caller supplies - including internal services on 127.0.0.1 and the cloud metadata endpoint at 169.254.169.254. Fixing it requires validating the URL and blocking internal address ranges before making any request.

Security Engineerpythonflaskssrf

What SSRF is and why it matters

Server-Side Request Forgery (SSRF) happens when a server fetches a URL chosen by the caller with no restrictions. A /fetch?url=... endpoint is the classic target. An attacker points it at http://127.0.0.1:8000/health to probe internal services, or at http://169.254.169.254/latest/meta-data/ to read cloud instance credentials - the server fetches it on their behalf, bypassing any network firewall.

Parsing and blocking internal destinations

Python's ipaddress module classifies addresses precisely. The pattern is to parse the URL, enforce a safe scheme, resolve the hostname, and reject the request if the target IP is in a private, loopback, or link-local range:

import socket
import ipaddress
from urllib.parse import urlparse

def _is_blocked(url):
    """Return True if the URL targets a loopback, private, or link-local address."""
    try:
        p = urlparse(url)
    except Exception:
        return True
    if p.scheme not in ("http", "https"):
        return True
    host = (p.hostname or "").lower()
    # Block by name before resolving.
    if host in ("localhost", "metadata.google.internal"):
        return True
    # IP literal: classify directly.
    try:
        ip = ipaddress.ip_address(host)
        return (ip.is_private or ip.is_loopback
                or ip.is_link_local or ip.is_reserved
                or ip.is_multicast)
    except ValueError:
        pass
    # Hostname: resolve and classify the resulting IP.
    try:
        ip = ipaddress.ip_address(socket.gethostbyname(host))
        return ip.is_private or ip.is_loopback or ip.is_link_local or ip.is_reserved
    except Exception:
        return False

Then guard the fetch endpoint:

@app.get("/fetch")
def fetch():
    url = request.args.get("url", "")
    if _is_blocked(url):
        return jsonify({"error": "blocked: refusing to fetch an internal address"}), 403
    resp = requests.get(url, timeout=3, allow_redirects=False)
    return jsonify({"status": resp.status_code, "body": resp.text[:2000]})

What each check covers

Limitations to know

allow_redirects=False matters: a redirect to http://127.0.0.1/ bypasses a one-time check. Without it, an attacker can redirect from a public URL to an internal one after your validation. Disable redirects entirely, or re-validate the Location header before following.

DNS rebinding is the other edge - a hostname resolves to a public IP at validation time, then to 127.0.0.1 by the time requests.get fires. A full defense resolves once, pins the IP, and connects to that IP directly with the Host header preserved. Blocking the obvious ranges is the essential first layer; egress firewall rules are the reliable backstop.

Want to try it hands-on? HeyDevJob gives you this exact setup in a live cloud workspace in your browser - edit it, run it, and see it work. Free, nothing to install.

Try it in a workspace →

What you'll practice

FAQ

What is an SSRF vulnerability?

Server-Side Request Forgery (SSRF) lets an attacker supply a URL that your server fetches on their behalf. This reaches internal services (127.0.0.1, 10.x, 192.168.x) and the cloud metadata endpoint (169.254.169.254) that are otherwise unreachable from the internet.

How do I block SSRF in Python?

Parse the URL with urlparse, enforce http/https scheme, then use Python's ipaddress module to check is_private, is_loopback, and is_link_local on the target IP. For hostnames, resolve them first with socket.gethostbyname before classifying. Return 403 if any check fails.

Why is blocking 169.254.169.254 important?

169.254.169.254 is the link-local cloud metadata endpoint available on AWS, GCP, and Azure instances. On a misconfigured instance it returns IAM credentials or API keys without authentication - SSRF to this address has been the entry point for major cloud data breaches.

Is SSRF the same as CSRF?

No. SSRF (Server-Side Request Forgery) tricks the server into making requests to internal targets it should not reach; CSRF tricks a user's browser into making authenticated requests. SSRF abuses the server, CSRF abuses the client session.

How do you remediate SSRF?

Validate the destination before fetching: resolve the hostname and reject loopback, private and internal IP ranges, and the cloud metadata IP 169.254.169.254. Prefer an allowlist of permitted hosts over a blocklist.

Keep learning

Close a SQL Injection in a Search EndpointSecurity projectNeutralize a Stored XSS in CommentsSecurity projectRemove Hardcoded Credentials From Source CodeSecurity projectSecurity roadmapStep by step to hiredSecurity interview questionsSTAR answersAll Security projectsProjects hub

Learn it by doing. Open this in a live cloud workspace, make the change yourself, and keep a record of the work you can share.

Open the workspace →