Insecure urllib.request.urlopen() Usage

MEDIUM

urllib.request.urlopen() over HTTP transmits data in plaintext. Verify HTTPS URLs are used and SSL context is properly configured.

Rule Information

Language
Python
Category
Python Core
Author
Shivasurya
Shivasurya
Last Updated
2026-03-22
Tags
pythonurlliburlopenplaintexthttpCWE-319OWASP-A02
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-LANG-SEC-061 --project .
1
2
3
4
5
6
7
8
rule.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

About This Rule

Understanding the vulnerability and how it is detected

urllib.request.urlopen() is Python's standard library function for making HTTP and HTTPS requests. When called with an HTTP (non-HTTPS) URL, all data is transmitted in plaintext. When called with an HTTPS URL but with an insecure SSL context (e.g., one created with ssl._create_unverified_context()), certificate verification is bypassed.

Unlike the requests library, urllib.request.urlopen() does not provide as clear a safety indicator for certificate verification. The SSL context must be explicitly created and passed, making it easier to accidentally use an insecure context.

This rule audits all urlopen() calls to ensure HTTPS URLs are used and that custom SSL contexts passed to the function do not disable certificate verification.

Security Implications

Potential attack scenarios if this vulnerability is exploited

1

Plaintext HTTP Transmission

When urlopen() is called with http:// URLs, all data including headers, authentication, and request/response bodies is transmitted without encryption, exposing it to network observers and MITM attackers.

2

Insecure SSL Context Bypass

Passing an SSL context created with ssl._create_unverified_context() to urlopen() disables certificate verification. The connection is encrypted but the server is not authenticated, enabling MITM attacks.

3

Redirect Following to HTTP

urllib.request.urlopen() follows HTTP redirects by default. An HTTPS connection that redirects to an HTTP URL may transmit the subsequent request (including cookies and headers) in plaintext.

4

No Timeout Default

urlopen() has no timeout by default, which can cause the application to hang indefinitely on slow or unresponsive servers, potentially enabling denial-of-service through resource exhaustion.

How to Fix

Recommended remediation steps

  • 1Ensure all urlopen() calls use https:// URLs; validate the URL scheme before calling urlopen().
  • 2Always pass a timeout argument to urlopen() to prevent hanging connections from causing resource exhaustion.
  • 3Pass an explicitly created ssl.create_default_context() to urlopen() for HTTPS requests to ensure certificate verification.
  • 4Consider using the requests library instead, which has cleaner API, better defaults, and easier certificate configuration.
  • 5Never pass ssl._create_unverified_context() or any context with verify_mode=CERT_NONE to urlopen().

Detection Scope

How Code Pathfinder analyzes your code for this vulnerability

This rule detects calls to urllib.request.urlopen() and urllib.urlopen() in Python source code. All call sites are flagged to prompt review of the URL scheme and SSL context configuration to ensure secure usage.

Compliance & Standards

Industry frameworks and regulations that require detection of this vulnerability

PCI DSS v4.0
Requirement 4.2.1 - Use strong cryptography for cardholder data transmission
OWASP Top 10
A02:2021 - Cryptographic Failures
NIST SP 800-52 Revision 2
TLS with certificate validation required for all sensitive data transmission
GDPR Article 32
Appropriate technical measures including encryption of personal data in transit

References

External resources and documentation

Similar Rules

Explore related security rules for Python

Frequently Asked Questions

Common questions about Insecure urllib.request.urlopen() Usage

Yes. Since Python 3.4.3, urllib.request.urlopen() with an HTTPS URL verifies certificates using the system CA store by default. However, if a custom SSL context is passed (especially one created with ssl._create_unverified_context()), verification is whatever the context specifies. Always use ssl.create_default_context() for custom contexts to maintain the secure default.
For application code, the requests library provides a cleaner API with better defaults for authentication, session management, retries, and certificate configuration. urllib.request is more appropriate for scripts where minimizing dependencies is important or when using urllib-specific features like custom handlers and openers.
Use urllib.request.Request() with a headers dict: req = Request(url, headers={"Authorization": f"Bearer {token}"}), then pass it to urlopen(). Alternatively, use urllib.request.HTTPBasicAuthHandler for basic auth, or the requests library which makes header management much simpler.
Use a timeout appropriate for the expected response time: 10-30 seconds for typical web APIs, up to 60-120 seconds for large file downloads or slow services. Always handle socket.timeout exceptions. Never omit the timeout parameter in production code.
urllib.request.urlopen() also supports file:// URLs for local filesystem access. Local file URLs do not involve network transmission and have different security considerations (path traversal). This rule focuses on HTTP/HTTPS security, but file:// URLs in urlopen() should also be reviewed for path traversal risks.
urllib.request.urlretrieve() has the same HTTP/HTTPS considerations as urlopen() and is additionally deprecated in Python 3. Replace it with urlopen() or the requests library. PYTHON-LANG-SEC-061 covers urlretrieve() as well.

New feature

Get these findings posted directly on your GitHub pull requests

The Insecure urllib.request.urlopen() Usage rule runs in CI and posts inline review comments on the exact lines — no dashboard, no SARIF viewer.

See how it works