SSRF

SSRF in Django remediation guide [May 2026] [GHSA-64cv-vxpr-j6vc]

[Updated May 2026] Updated GHSA-64cv-vxpr-j6vc

Overview

SSRF (Server-Side Request Forgery) can allow an attacker to force the Django application to perform requests to internal or restricted services from the server. In cloud or on-prem deployments, this may expose internal APIs, service endpoints, or cloud metadata endpoints that are not reachable from the public internet. Depending on the app's privileges and the target network, successful SSRF can enable data exfiltration, access to sensitive resources, or lateral movement within a protected environment. Applications that process user-supplied URLs or perform content fetching without strict validation are especially at risk, since an attacker can steer server traffic toward unintended destinations. In Django, SSRF surfaces when a view, task, or utility fetches resources from a URL provided by a user or third party. Examples include remote image or file fetchers, content validators, webhooks, or admin features that download remote content. If the code uses requests, urllib, or similar libraries to fetch the URL without proper validation, an attacker can trigger calls to internal endpoints (such as internal APIs or metadata services) or to external resources that reveal information or expose services inadvertently. Properly securing these patterns is essential to prevent exposure of internal networks and sensitive data. Remediation requires defense-in-depth: validate and constrain outbound URLs, implement a strict allowlist of safe hosts, and use Django utilities to enforce host and scheme checks. Prefer remote fetch only when necessary, with timeouts and proper error handling. Isolate remote-fetch logic behind a proxy or restricted network, enable egress controls, log SSRF attempts, and keep dependencies up to date with security advisories. Add targeted tests that simulate malicious URLs to verify enforcement across endpoints.

Code Fix Example

Django API Security Remediation
# Vulnerable pattern: fetch without validation
import requests
from django.http import HttpResponse, HttpResponseBadRequest

def vulnerable_view(request):
    url = request.GET.get('url')
    if not url:
        return HttpResponseBadRequest('Missing url')
    resp = requests.get(url, timeout=5)
    if resp.status_code != 200:
        return HttpResponseBadRequest('Failed to fetch')
    return HttpResponse(resp.content, content_type=resp.headers.get('Content-Type', 'application/octet-stream'))

# Fixed pattern: validate host/scheme and restrict to allowlisted hosts
import requests
from urllib.parse import urlparse
from django.conf import settings
from django.http import HttpResponse, HttpResponseBadRequest
from django.utils.http import url_has_allowed_host_and_scheme

ALLOWED_FETCH_HOSTS = {'api.example.internal', 'cdn.example.internal'}

def safe_view(request):
    url = request.GET.get('url')
    if not url:
        return HttpResponseBadRequest('Missing url')
    # Validate against Django's host/scheme policy and explicit allowlist
    if not url_has_allowed_host_and_scheme(url, allowed_hosts=settings.ALLOWED_HOSTS, require_https=False):
        return HttpResponseBadRequest('Disallowed host or scheme')
    parsed = urlparse(url)
    if parsed.hostname not in ALLOWED_FETCH_HOSTS:
        return HttpResponseBadRequest('Host not allowed')
    resp = requests.get(url, timeout=5)
    if resp.status_code != 200:
        return HttpResponseBadRequest('Failed to fetch')
    return HttpResponse(resp.content, content_type=resp.headers.get('Content-Type', 'application/octet-stream'))

CVE References

Choose which optional cookies to allow. You can change this any time.