PYTHON

Prevent Server-Side Request Forgery (SSRF) in Python

Secure your Python application against SSRF vulnerabilities by validating URLs and restricting outbound requests to only trusted domains and protocols.

import urllib.parse
import requests
from ipaddress import ip_address, IPv4Address, IPv6Address

# Whitelist of allowed domains/IPs
TRUSTED_HOSTS = [
    "api.example.com",
    "cdn.example.org"
]

# Optionally, whitelist of allowed IP ranges (e.g., public IPs only)
# This is more complex and might involve checking against private IP ranges.
# For simplicity, we'll focus on hostname-based whitelist for common cases.

def is_valid_url_for_ssrf(url: str) -> bool:
    """
    Validates a URL to prevent SSRF attacks.
    Checks for valid scheme, trusted host, and non-private IP resolution.
    """
    try:
        parsed_url = urllib.parse.urlparse(url)

        # 1. Check scheme (only http/https allowed)
        if parsed_url.scheme not in ["http", "https"]:
            return False

        # 2. Check for empty host (e.g., //example.com/...)
        if not parsed_url.hostname:
            return False

        # 3. Check against trusted hosts whitelist
        if parsed_url.hostname not in TRUSTED_HOSTS:
            # Optionally, resolve IP and check if it's public.
            # This requires DNS resolution, which can be slow and itself a target.
            # For high security, it's better to stick to a strict hostname whitelist.
            return False

        # (Advanced) Optional: Check for private IP ranges after DNS resolution
        # This requires careful implementation and consideration of IPv6.
        # Example:
        # from socket import gethostbyname
        # try:
        #     ip = ip_address(gethostbyname(parsed_url.hostname))
        #     if ip.is_private: # IPv4/IPv6 is_private check
        #         return False
        # except Exception: # DNS resolution failed or not an IP
        #     return False

        return True

    except ValueError:
        return False

def fetch_data_securely(url: str) -> str:
    if not is_valid_url_for_ssrf(url):
        raise ValueError("Attempted SSRF attack: Invalid URL or untrusted host.")
    
    print(f"Fetching data from: {url}")
    response = requests.get(url, timeout=5) # Add a timeout
    response.raise_for_status() # Raise an exception for HTTP errors
    return response.text

# Example Usage:
try:
    # Valid URL
    print(fetch_data_securely("https://api.example.com/data"))
except ValueError as e:
    print(f"Error: {e}")

try:
    # Invalid scheme
    fetch_data_securely("file:///etc/passwd")
except ValueError as e:
    print(f"Error: {e}")

try:
    # Untrusted host
    fetch_data_securely("http://bad-actor.com/secret")
except ValueError as e:
    print(f"Error: {e}")

try:
    # Private IP (if implemented, otherwise this might pass without explicit check)
    # Note: 'localhost' usually resolves to 127.0.0.1, which is private.
    # This will be caught by TRUSTED_HOSTS for this example.
    fetch_data_securely("http://127.0.0.1/admin")
except ValueError as e:
    print(f"Error: {e}")
How it works: This Python snippet demonstrates a robust method to prevent Server-Side Request Forgery (SSRF) attacks by validating URLs before making outbound requests. The `is_valid_url_for_ssrf` function checks several criteria: allowing only HTTP/HTTPS schemes, ensuring a non-empty hostname, and most critically, whitelisting allowed domains against `TRUSTED_HOSTS`. This approach prevents an attacker from tricking your server into accessing internal resources or unauthorized external services, thus safeguarding your infrastructure.

Need help integrating this into your project?

Our team of expert developers can help you build your custom application from scratch.

Hire DigitalCodeLabs