← Back to all snippets
PYTHON

Validate URLs to Prevent Server-Side Request Forgery (SSRF)

Protect your web application from SSRF attacks by strictly validating URLs before making server-side requests. This Python snippet shows how to check hostnames.

import urllib.parse
import ipaddress

def is_safe_url(url: str, allowed_schemes: list[str] = ['http', 'https'], allow_private_ips: bool = False) -> bool:
    """
    Validates a URL to prevent Server-Side Request Forgery (SSRF).
    Ensures the URL uses allowed schemes and does not point to internal resources.
    """
    try:
        parsed_url = urllib.parse.urlparse(url)

        # 1. Check scheme
        if parsed_url.scheme not in allowed_schemes:
            print(f"Unsafe scheme: {parsed_url.scheme}")
            return False

        # 2. Check hostname/IP
        hostname = parsed_url.hostname
        if not hostname:
            print("No hostname found.")
            return False # Or handle as per policy (e.g., allow relative paths for local assets)

        # Resolve hostname to IP addresses
        try:
            # Use socket.gethostbyname_ex to get all IPs for a hostname
            # For simplicity here, just using gethostbyname, but for production,
            # consider resolving all IPs and checking them.
            ip_address_str = ipaddress.ip_address(hostname)
        except ValueError:
            # Not an IP address, resolve hostname
            import socket
            try:
                ip_address_str = socket.gethostbyname(hostname)
            except socket.gaierror:
                print(f"Could not resolve hostname: {hostname}")
                return False

        ip_addr = ipaddress.ip_address(ip_address_str)

        # 3. Prevent access to private/reserved IPs
        if not allow_private_ips:
            if ip_addr.is_private or ip_addr.is_loopback or ip_addr.is_reserved:
                print(f"Blocked private/loopback/reserved IP: {ip_addr}")
                return False

        # Add any other specific blacklist/whitelist checks for hostnames/IPs
        # Example: if hostname == "evil.com": return False

        return True

    except Exception as e:
        print(f"URL parsing error: {e}")
        return False

if __name__ == "__main__":
    print(f"Validating http://example.com: {is_safe_url('http://example.com')}")
    print(f"Validating https://google.com: {is_safe_url('https://google.com')}")
    print(f"Validating ftp://malicious.com: {is_safe_url('ftp://malicious.com')}")
    print(f"Validating http://127.0.0.1/admin: {is_safe_url('http://127.0.0.1/admin')}")
    print(f"Validating http://localhost:8080: {is_safe_url('http://localhost:8080')}")
    print(f"Validating http://192.168.1.1/internal: {is_safe_url('http://192.168.1.1/internal')}")
    print(f"Validating http://192.168.1.1/internal (allow private): {is_safe_url('http://192.168.1.1/internal', allow_private_ips=True)}")
    print(f"Validating http://[::1]/internal: {is_safe_url('http://[::1]/internal')}") # IPv6 loopback
How it works: This Python function provides server-side validation for URLs to prevent Server-Side Request Forgery (SSRF) attacks. It parses a given URL, checks if its scheme is allowed (e.g., only `http` and `https`), and crucially, verifies that the resolved IP address of the hostname does not belong to private, loopback, or reserved IP ranges, thus preventing attackers from tricking the server into accessing internal network resources.

Need help integrating this into your project?

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

Hire DigitalCodeLabs