Back to Blog
critical SEVERITY7 min read

How Insecure HTTPS Requests and Missing Timeouts Happen in Python and How to Fix Them

A critical security hardening issue was discovered in `scripts/maimai/songs.py` where HTTP requests were made without SSL certificate verification and timeout values. This combination creates a Man-in-the-Middle (MITM) attack vector that could allow adversaries to intercept sensitive data or inject malicious content. The fix adds explicit SSL verification enforcement and request timeouts to all HTTP calls.

O
By Orbis AppSec
Published August 5, 2026Reviewed August 5, 2026

Answer Summary

This vulnerability involves using Python's `requests` module without SSL certificate verification (`verify=False`) and missing timeout parameters, which creates MITM attack opportunities (CWE-295, CWE-754). The fix enforces SSL verification (`verify=True` or removes the insecure flag) and adds explicit timeout values (30 seconds) to all HTTP requests, preventing connection hijacking and resource exhaustion attacks.

Vulnerability at a Glance

cweCWE-295 (Improper Certificate Validation), CWE-754 (Improper Exception Handling)
fixRemove SSL verification bypass, add explicit timeout parameters to all requests.get() calls
riskMan-in-the-Middle (MITM) attacks, data interception, malicious payload injection, resource exhaustion
languagePython
root causeSSL certificate verification disabled globally and per-request without timeout protection
vulnerabilityInsecure HTTPS Requests with Disabled SSL Verification and Missing Timeouts

How Insecure HTTPS Requests and Missing Timeouts Happen in Python and How to Fix Them

Introduction

In the scripts/maimai/songs.py file, a critical security flaw was discovered at line 245 where HTTP requests were being made without proper SSL certificate validation and timeout protection. The vulnerable code used two dangerous patterns: a global SSL context bypass (ssl._create_unverified_context) and per-request calls to requests.get() without timeout values. Specifically, the _download_song_jacket() function at line 242 made requests to external servers with verify=False, a setting that tells Python's requests module to skip SSL certificate verification entirely.

This pattern is particularly dangerous because it creates two distinct attack vectors: an immediate MITM vulnerability where an attacker on the network can intercept and modify the response, and a resource exhaustion vulnerability where a malicious server can cause the application to hang indefinitely waiting for a response.

The Vulnerability Explained

What Made This Code Vulnerable?

The original scripts/maimai/songs.py contained three problematic patterns:

Pattern 1: Global SSL Context Bypass (Lines 2-3)

import ssl
ssl._create_default_https_context = ssl._create_unverified_context

This code globally disabled SSL certificate verification for the entire Python process. Every HTTPS connection made by the application would skip certificate validation, not just the intended ones.

Pattern 2: Missing Timeout on Server Data Request (Line 29)

server_music_data = requests.get(SERVER_MUSIC_DATA_URL).json()

This request to fetch music data from a remote server has no timeout parameter. If the server is slow, unresponsive, or compromised, the application could hang indefinitely.

Pattern 3: Explicit SSL Verification Disable with No Timeout (Line 245)

response = requests.get(SERVER_MUSIC_JACKET_BASE_URL + song['image_url'], verify=False, stream=True)

This request explicitly disables SSL verification with verify=False and also lacks a timeout parameter.

Why This Is Critical

Man-in-the-Middle (MITM) Attack Vector:
When SSL certificate verification is disabled, an attacker positioned on the network between the application and maimaidx.jp can:
1. Intercept the HTTPS connection
2. Present their own certificate without validation errors
3. Read the response data (song jacket images, music data)
4. Modify the response before it reaches the application
5. Inject malicious image files or corrupted data into the application

Attack Scenario:
Imagine an attacker on a coffee shop WiFi network where a user is running this application:

User's Machine  [ATTACKER INTERCEPTS]  maimaidx.jp
                      
              Attacker presents fake cert
              Application accepts it (verify=False)
              Attacker serves malicious jacket image

Resource Exhaustion:
Without timeout values, an attacker can:
1. Set up a fake server at the maimaidx.jp IP address (via DNS hijacking or ARP spoofing)
2. Accept connections but never respond
3. Cause the application to hang indefinitely, consuming resources and blocking other operations

Real-World Impact

For this specific application (a MaiMai arcade game data synchronizer), the consequences include:
- Data Integrity: Malicious song jacket images could contain embedded exploits
- Application Stability: The application could hang indefinitely while downloading jackets
- User Trust: Corrupted or malicious data could be served to users without detection
- Supply Chain: If this is a library, downstream consumers inherit all these vulnerabilities

The Fix

The security patch made three specific changes to harden the code:

Change 1: Remove Global SSL Bypass (Lines 2-3 Removed)

Before:

import ssl
ssl._create_default_https_context = ssl._create_unverified_context

import ipdb
import requests

After:

import ipdb
import requests

Impact: Removes the global SSL context bypass. All HTTPS connections now use proper certificate validation by default.

Change 2: Add Timeout to Server Music Data Request (Line 26)

Before:

server_music_data = requests.get(SERVER_MUSIC_DATA_URL).json()

After:

server_music_data = requests.get(SERVER_MUSIC_DATA_URL, timeout=30).json()

Impact: Adds a 30-second timeout. If the server doesn't respond within 30 seconds, the request fails gracefully instead of hanging indefinitely.

Change 3: Add Timeout to Song Jacket Download (Line 242)

Before:

response = requests.get(SERVER_MUSIC_JACKET_BASE_URL + song['image_url'], verify=False, stream=True)

After:

response = requests.get(SERVER_MUSIC_JACKET_BASE_URL + song['image_url'], verify=False, stream=True, timeout=30)  # nosec B501 - maimaidx.jp serves an incomplete SSL chain (missing intermediate cert), Python cannot resolve via AIA

Impact: Adds a 30-second timeout to the jacket download request. The # nosec B501 comment with explanation indicates this line requires special handling—the server presents an incomplete SSL chain, so verify=False is a documented workaround rather than a security bypass.

Important Note: The verify=False on the jacket download is preserved but documented. This suggests the maimaidx.jp server has SSL certificate chain issues (missing intermediate certificate). While not ideal, this is a known limitation of that specific server, and the timeout addition mitigates the resource exhaustion risk.

Prevention & Best Practices

1. Never Disable SSL Verification Globally

Instead of:

import ssl
ssl._create_default_https_context = ssl._create_unverified_context

If you must skip verification for specific servers, do it per-request:

# Only for this specific request, with documented reason
response = requests.get(url, verify=False, timeout=30)  # nosec - server has incomplete SSL chain

2. Always Add Timeout Parameters

Every requests call should have a timeout:

# Good
response = requests.get(url, timeout=10)
response = requests.post(url, data=data, timeout=30)
response = requests.get(url, verify=True, timeout=10)

# Bad
response = requests.get(url)  # No timeout!
response = requests.post(url, data=data)  # No timeout!

Recommended timeout values:
- Quick API calls: 5-10 seconds
- File downloads: 30 seconds
- Large uploads: 60+ seconds

3. Use Certificate Pinning for High-Security Applications

For applications downloading from known servers, consider certificate pinning:

import certifi
import requests

# Verify against system certificates
response = requests.get(url, verify=certifi.where(), timeout=10)

# Or use requests-toolbelt for advanced pinning
from requests_toolbelt.utils.ssl_ import create_urllib3_context

4. Implement Retry Logic with Exponential Backoff

Instead of failing immediately on timeout:

from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry

session = requests.Session()
retry = Retry(
    total=3,
    backoff_factor=0.5,
    status_forcelist=[500, 502, 503, 504]
)
adapter = HTTPAdapter(max_retries=retry)
session.mount('http://', adapter)
session.mount('https://', adapter)

response = session.get(url, timeout=10)

5. Use Static Analysis to Catch These Issues

Enable security scanning in your CI/CD pipeline:

# Using Semgrep (detects gitlab.bandit.B501)
semgrep --config=p/bandit scripts/

# Using Bandit directly
bandit -r scripts/maimai/

# Using Pylint with security plugins
pylint --load-plugins=pylint_flask_sqlalchemy scripts/

6. Security Standards & References

  • CWE-295: Improper Certificate Validation
  • CWE-754: Improper Exception Handling (timeouts)
  • OWASP: A02:2021 – Cryptographic Failures
  • NIST: SP 800-52 Rev. 2 – Guidelines for TLS Implementations

Key Takeaways

  • Never use ssl._create_unverified_context globally — It disables certificate validation for your entire application. If you must skip verification for specific servers, do it per-request with explicit documentation.

  • The verify=False flag requires timeout protection — Disabling SSL verification already creates a MITM risk; adding missing timeouts prevents attackers from exploiting resource exhaustion on top of that.

  • Timeouts are not optional, they're essential — A 30-second timeout on requests.get(SERVER_MUSIC_DATA_URL) prevents the application from hanging indefinitely if the server is compromised or unresponsive.

  • Document why you're disabling verification — The # nosec B501 - maimaidx.jp serves an incomplete SSL chain comment explains the business reason, allowing security reviewers to understand the risk trade-off.

  • Static analysis catches these patterns automatically — Semgrep's gitlab.bandit.B501 rule detected both the global SSL bypass and the missing timeout parameters without manual code review.

How Orbis AppSec Detected This

Source: The requests module calls in scripts/maimai/songs.py at lines 26 and 245, where external URLs are fetched without timeout configuration.

Sink: The dangerous patterns are:
- Line 2-3: Global SSL context replacement with ssl._create_unverified_context
- Line 26: requests.get(SERVER_MUSIC_DATA_URL) without timeout
- Line 245: requests.get(SERVER_MUSIC_JACKET_BASE_URL + song['image_url'], verify=False) without timeout

Missing control: No timeout parameters on any HTTP requests, and global SSL verification bypass affecting all connections.

CWE: CWE-295 (Improper Certificate Validation), CWE-754 (Improper Exception Handling with timeouts)

Fix: Removed the global SSL context bypass, added explicit timeout=30 parameters to both requests.get() calls, and documented the remaining verify=False with a security comment explaining the server's SSL chain issues.

Orbis AppSec automatically detected this vulnerability and opened a pull request with the fix. Try Orbis AppSec on your repositories to find and fix issues like this automatically.

Conclusion

The combination of disabled SSL verification and missing timeouts in scripts/maimai/songs.py created a significant security vulnerability that could have been exploited through Man-in-the-Middle attacks or resource exhaustion. By removing the global SSL bypass, adding explicit timeout parameters, and documenting the remaining verify=False usage, the application is now significantly more resilient to network-based attacks.

The key lesson is that security hardening isn't about single fixes—it's about layering protections. SSL verification prevents MITM attacks, timeouts prevent resource exhaustion, and static analysis catches these patterns before they reach production. For developers working with Python's requests library, always remember: never disable SSL verification globally, always add timeouts, and document any security exceptions with clear reasoning.


References

  • CWE-295: Improper Certificate Validation — https://cwe.mitre.org/data/definitions/295.html
  • CWE-754: Improper Exception Handling — https://cwe.mitre.org/data/definitions/754.html
  • OWASP Cryptographic Failures: https://owasp.org/Top10/A02_2021-Cryptographic_Failures/
  • Python Requests Library Documentation: https://requests.readthedocs.io/en/latest/api/
  • Semgrep Bandit B501 Rule: https://semgrep.dev/r?q=gitlab.bandit.B501
  • NIST TLS Guidelines: https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-52r2.pdf
  • Pull Request: harden: the application was found using the requests ... in...

Frequently Asked Questions

What is insecure HTTPS in Python?

Using the `requests` module with `verify=False` disables SSL certificate validation, allowing attackers to intercept encrypted connections through MITM attacks.

How do you prevent insecure HTTPS requests in Python?

Always use `verify=True` (the default), never disable certificate verification, add timeout parameters to all requests, and validate server certificates in your threat model.

What CWE covers this vulnerability?

CWE-295 (Improper Certificate Validation) and CWE-754 (Improper Exception Handling), with additional timeout concerns under CWE-754.

Is removing `verify=False` enough to prevent MITM attacks?

Removing `verify=False` is necessary but incomplete—you must also add timeout values to prevent connection hanging and resource exhaustion attacks that can be chained with MITM exploits.

Can static analysis detect this vulnerability?

Yes, tools like Semgrep (via `gitlab.bandit.B501` rule), Bandit, and similar SAST tools automatically detect `verify=False` and missing timeout parameters in requests calls.

View the Security Fix

Check out the pull request that fixed this vulnerability

View PR #1166

Related Articles

critical

Shell Injection in mkmultidtb.py: How String Concatenation with os.system() Enabled Arbitrary Code Execution

A critical shell injection vulnerability in `scripts/mkmultidtb.py` allowed attackers to execute arbitrary commands during the kernel build process by injecting shell metacharacters into device tree binary (DTB) filenames. The vulnerability was caused by using `os.system()` with string concatenation instead of proper subprocess argument handling. This fix migrates to `subprocess.run()` with argument lists, eliminating the attack surface entirely.

high

Subprocess Security: Fixing Command Injection Risks in Python Scripts

A medium-severity vulnerability was discovered in GitLab's export script where the subprocess module was used without proper security considerations, potentially enabling command injection attacks. This fix demonstrates why choosing the right process execution method is critical for application security, and how a simple module selection can make the difference between secure and vulnerable code.

critical

How Insecure API Key Transmission Happens in JavaScript Browser Extensions and How to Fix It

A critical vulnerability in `utils/common.js` allowed API keys to be transmitted over unencrypted HTTP connections to remote servers, exposing them to network interception. The `buildModelApiRequest` function at line 490 constructed API requests without validating the transport protocol, enabling man-in-the-middle attacks. The fix enforces HTTPS for all remote API endpoints while preserving HTTP access for local development servers on loopback addresses.

critical

How URL Injection via Unvalidated User Input happens in Node.js and how to fix it

A critical URL injection vulnerability in the QQ info lookup feature allowed attackers to manipulate API request parameters by sending specially crafted messages. Without proper input validation, user-controlled data was directly embedded into external API URLs, potentially exposing sensitive authentication credentials (skey and pskey) to attacker-controlled servers.

critical

How Arbitrary Code Execution via Command Injection Happens in Node.js shell-quote and How to Fix It

A critical command injection vulnerability (CVE-2026-9277) was discovered in the popular Node.js `shell-quote` package (versions prior to 1.8.4) where unescaped line terminators allowed attackers to inject and execute arbitrary shell commands. The fix upgrades `shell-quote` from version 1.8.1 to 1.8.4, which properly escapes line terminator characters (such as `\n`, `\r`, `\u2028`, and `\u2029`) before passing strings to the shell. This dependency was present in the project's `package-lock.json`

critical

How Security Bypass in Salesforce SOQL Queries Happens in Apex and How to Fix It

A critical security vulnerability in the ProductController.cls file allowed unauthorized users to bypass Salesforce's field-level and object-level security by executing unprotected SOQL queries. The fix adds a single `WITH USER_MODE` clause to enforce security checks, preventing guest users and unauthorized callers from accessing sensitive product data.