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.