Django SSRF via urllib

HIGH

User input flows to urllib.request.urlopen() or urllib.request.Request(), enabling the server to make HTTP requests to attacker-controlled URLs.

Rule Information

Language
Python
Category
Django
Author
Shivasurya
Shivasurya
Last Updated
2026-03-22
Tags
pythondjangossrfurllibhttp-clientstandard-librarytaint-analysisinter-proceduralCWE-918OWASP-A10
CWE References

Interactive Playground

Experiment with the vulnerable code and security rule below. Edit the code to see how the rule detects different vulnerability patterns.

pathfinder scan --ruleset python/PYTHON-DJANGO-SEC-031 --project .
1
2
3
4
5
6
rule.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43

About This Rule

Understanding the vulnerability and how it is detected

This rule detects Server-Side Request Forgery (SSRF) vulnerabilities in Django applications where untrusted user input from HTTP request parameters flows into urllib.request.urlopen() or urllib.request.Request() from Python's standard library.

urllib is the standard Python HTTP client and is commonly used in Django applications that do not have the third-party requests library as a dependency, or in older codebases. It is subject to the same SSRF risks as any HTTP client: an attacker who controls the URL can direct server-side requests to cloud metadata endpoints, internal services, localhost, or file:// URLs.

urllib's urlopen() additionally supports several URL schemes beyond http/https, including file:// and ftp://, which increases the attack surface compared to some other HTTP clients. The file:// scheme in particular can be used to read arbitrary files from the server's filesystem.

Security Implications

Potential attack scenarios if this vulnerability is exploited

1

Cloud Metadata Service Access via Standard Library

urllib.request.urlopen('http://169.254.169.254/latest/meta-data/iam/...') fetches AWS IAM credentials from the instance metadata service. Unlike the requests library, urllib is always available in any Python environment, making this vector present in any Django deployment regardless of installed packages.

2

Local File Disclosure via file:// Scheme

urllib.request.urlopen() supports the file:// URL scheme by default. An attacker can supply a URL like file:///etc/passwd or file:///app/settings.py to read arbitrary files from the server filesystem, potentially exposing Django's SECRET_KEY, database credentials, and other configuration secrets.

3

Internal Service Access and Port Scanning

The application server can reach internal network services that are not exposed to the internet. An attacker can use urlopen() to scan internal ports (observing connection errors vs. successful responses) and access internal HTTP services without authentication.

4

Gopher Protocol Exploitation

Some versions of urllib support the gopher:// scheme which can be used for SSRF attacks against Redis, Memcached, and other services that use text-based protocols. If gopher:// is accessible, attackers can send arbitrary TCP payloads to internal services.

How to Fix

Recommended remediation steps

  • 1Validate user-supplied URLs against a strict allowlist of trusted domains before calling urlopen().
  • 2Block the file://, gopher://, and ftp:// schemes -- accept only http and https.
  • 3Block requests to private IP ranges, loopback addresses, and link-local networks (169.254.0.0/16).
  • 4Resolve hostnames to IPs at validation time to prevent DNS rebinding attacks from bypassing scheme and domain checks.
  • 5Consider replacing urllib with the requests library and its higher-level session management, which makes timeout and redirect controls more explicit.

Detection Scope

How Code Pathfinder analyzes your code for this vulnerability

This rule performs inter-procedural taint analysis with global scope. Sources include calls("request.GET.get"), calls("request.POST.get"), calls("request.GET.__getitem__"), calls("request.POST.__getitem__"), calls("request.body"), and calls("request.read"). Sinks include calls("urllib.request.urlopen"), calls("urllib.request.Request"), and calls("urllib.urlopen") with tainted URL argument tracked via .tracks(0). Sanitizers include URL validation functions that check against allowlists and block private IP ranges and dangerous schemes. The rule follows taint across file and module boundaries.

Compliance & Standards

Industry frameworks and regulations that require detection of this vulnerability

OWASP Top 10
A10:2021 - Server-Side Request Forgery (SSRF)
CWE Top 25
CWE-918 - Server-Side Request Forgery in dangerous weaknesses list
PCI DSS v4.0
Requirement 6.2.4 - protect against injection attacks including SSRF
NIST SP 800-53
SC-7: Boundary Protection; SI-10: Information Input Validation
ISO 27001
A.13.1.3: Segregation in networks; A.14.2.5: Secure engineering principles

References

External resources and documentation

Similar Rules

Explore related security rules for Python

Frequently Asked Questions

Common questions about Django SSRF via urllib

Python's urllib.request.urlopen() was designed as a general URL opener supporting multiple schemes including file://, ftp://, and http://. In an SSRF context, file:// support is critical to block because it allows attackers to read arbitrary files from the server using a URL like file:///etc/passwd or file:///app/settings.py. Always restrict accepted schemes to http and https only.
SEC-031 targets Python's standard library urllib.request module, which is available without any third-party dependencies. SEC-030 targets the requests library (pip install requests). Both implement SSRF but with different APIs. Some Django projects use urllib for simple HTTP calls to avoid adding a dependency; others use requests for more ergonomic HTTP handling. Both need the same URL validation logic.
Both. urllib is used directly in Django management commands, middleware, and utility functions that perform simple HTTP calls without the complexity of session management. It is also prevalent in older Django applications predating widespread requests library adoption, and in code that avoids third-party dependencies for security or licensing reasons.
Partial protection only. Checking that the scheme is 'http' or 'https' blocks file://, gopher://, and ftp:// attacks but does not prevent requests to internal IP ranges, cloud metadata, or localhost. A complete SSRF defense requires both scheme validation AND IP range blocking (or strict domain allowlisting with hostname resolution at validation time).
Yes, and those secondary calls create SSRF risk if user input influences what URL is fetched. Review all urllib submodule usages where user-controlled data could influence the URL argument, not just direct urlopen() calls.
URL parsing and IP range checking are microsecond-level operations. Network DNS resolution for hostname-to-IP validation at blocking time adds one DNS lookup per request, which is typically 1-10ms. This is negligible compared to the network round-trip of the HTTP request itself. The security benefit far outweighs the performance cost.

New feature

Get these findings posted directly on your GitHub pull requests

The Django SSRF via urllib rule runs in CI and posts inline review comments on the exact lines — no dashboard, no SARIF viewer.

See how it works