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.


Prevention and further reading

Frequently Asked Questions

What CWE covers this vulnerability?

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

View the Security Fix

Check out the pull request that fixed this vulnerability

View PR #1166

Related Articles

critical

deleteNestedProperty Prototype Pollution via Dot-Notation Path

The `deleteNestedProperty` function in propertyUtils.ts allowed attackers to manipulate JavaScript object prototypes by passing specially crafted dot-notation paths like `__proto__.polluted`. A fix now blocks dangerous keys before processing, preventing prototype pollution attacks that could affect all objects in the application.

high

How Denial of Service via Infinite Loop Happens in JavaScript Dependencies and How to Fix It

CVE-2026-67213 is a high-severity denial of service vulnerability in nanoid before version 5.1.6 that triggers an infinite loop during random ID generation when processing specially crafted input. We upgraded nanoid across the entire dependency tree to patch this flaw and prevent attackers from freezing application threads. This fix ensures that ID generation remains resilient even when handling adversarial input patterns.

high

How Sensitive Data Exposure happens in Zotero plugins and how to fix it

A high-severity data exposure vulnerability in `Zotero.ts` automatically transmitted complete document metadata—including private notes, attachment paths, and tags—to external LLM services without user consent. The fix replaces broad `item.toJSON()` serialization with explicit field selection, sending only essential bibliographic data.

high

How missing dependency update cooldowns happen in GitHub Dependabot configurations and how to fix it

A semgrep scan flagged `.github/dependabot.yml` for lacking a cooldown period, meaning Dependabot would immediately propose updates to brand-new package versions across npm, Bundler, and Docker ecosystems. The fix adds a `cooldown: default-days: 7` block to every `package-ecosystem` entry, forcing a one-week waiting period before newly published releases are considered — reducing exposure to malicious or unstable package drops.

high

How Path Traversal Happens in TensorFlow's Data Service and How to Fix It

TensorFlow's data service dispatcher validated dataset IDs against forward-slash traversal attacks but overlooked backslash characters on non-Windows platforms, allowing attackers to escape the root directory. A targeted fix adds explicit backslash validation across all platforms, closing a high-severity path traversal vulnerability in the snapshot management system.

critical

How Unbounded WebSocket Message Handling Causes Resource Exhaustion in Node.js and How to Fix It

The WebSocketCrossServerAdapter class in a popular Node.js WebSocket library lacked any rate limiting on inbound messages, allowing attackers to flood Redis nodes and WebSocket servers with high-volume traffic. The fix introduces a configurable `rateLimit` option that caps messages per connection per second, preventing resource exhaustion while preserving legitimate functionality.