Python Requests Disabled Certificate Validation Vulnerability

High Risk Insecure Transport
PythonRequestsSSLTLSCertificate ValidationMITMTransport Security

What it is

Application disables SSL/TLS certificate verification in requests, making it vulnerable to man-in-the-middle attacks and connection interception.

import requests from flask import Flask, request import urllib3 # Vulnerable: Disable SSL warnings urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) @app.route('/api_call') def api_call(): # Vulnerable: Certificate verification disabled url = 'https://api.internal.com/data' response = requests.get(url, verify=False) # DANGEROUS return response.json() @app.route('/webhook', methods=['POST']) def webhook(): # Vulnerable: POST without certificate validation webhook_url = 'https://external-webhook.com/notify' data = request.get_json() # Dangerous: MITM attacks possible response = requests.post( webhook_url, json=data, verify=False # Certificate validation disabled ) return {'status': response.status_code} # Vulnerable: Global session with disabled verification session = requests.Session() session.verify = False # All requests vulnerable
import requests from flask import Flask, request import ssl import certifi from requests.adapters import HTTPAdapter from urllib3.util.ssl_ import create_urllib3_context # Secure SSL context configuration def create_secure_context(): """Create secure SSL context with proper validation.""" context = ssl.create_default_context(cafile=certifi.where()) context.check_hostname = True context.verify_mode = ssl.CERT_REQUIRED context.minimum_version = ssl.TLSVersion.TLSv1_2 return context # Secure session configuration def create_secure_session(): """Create requests session with secure SSL configuration.""" session = requests.Session() # Ensure certificate verification is enabled session.verify = True # Explicit verification # Use latest CA bundle session.verify = certifi.where() # Configure secure headers session.headers.update({ 'User-Agent': 'SecureApp/1.0', 'Accept': 'application/json' }) return session @app.route('/api_call') def api_call(): """Secure API call with certificate validation.""" try: url = 'https://api.internal.com/data' # Secure: Certificate verification enabled response = requests.get( url, verify=True, # Explicit verification timeout=10, headers={'Accept': 'application/json'} ) response.raise_for_status() return response.json() except requests.exceptions.SSLError as e: return {'error': f'SSL verification failed: {str(e)}'}, 400 except requests.exceptions.RequestException as e: return {'error': f'Request failed: {str(e)}'}, 500 @app.route('/webhook', methods=['POST']) def webhook(): """Secure webhook with certificate validation.""" try: webhook_url = 'https://external-webhook.com/notify' data = request.get_json() if not data: return {'error': 'No data provided'}, 400 # Secure: Full certificate validation response = requests.post( webhook_url, json=data, verify=certifi.where(), # Use trusted CA bundle timeout=15, headers={ 'Content-Type': 'application/json', 'User-Agent': 'SecureWebhookClient/1.0' } ) response.raise_for_status() return { 'status': 'success', 'response_code': response.status_code } except requests.exceptions.SSLError as e: return {'error': f'SSL verification failed: {str(e)}'}, 400 except requests.exceptions.RequestException as e: return {'error': f'Webhook failed: {str(e)}'}, 500 # Secure: Session with certificate validation secure_session = create_secure_session() @app.route('/secure_session_example') def secure_session_example(): """Example using secure session.""" try: response = secure_session.get( 'https://api.example.com/secure-endpoint', timeout=10 ) response.raise_for_status() return response.json() except requests.exceptions.RequestException as e: return {'error': str(e)}, 500 # Advanced: Certificate pinning class CertificatePinningAdapter(HTTPAdapter): """HTTP adapter with certificate pinning.""" def __init__(self, expected_fingerprint, *args, **kwargs): self.expected_fingerprint = expected_fingerprint super().__init__(*args, **kwargs) def init_poolmanager(self, *args, **kwargs): ctx = create_urllib3_context() ctx.check_hostname = True ctx.verify_mode = ssl.CERT_REQUIRED kwargs['ssl_context'] = ctx return super().init_poolmanager(*args, **kwargs) @app.route('/pinned_request') def pinned_request(): """Example with certificate pinning.""" try: session = requests.Session() # Pin certificate for critical connections expected_fingerprint = 'sha256:...' # Your expected certificate fingerprint adapter = CertificatePinningAdapter(expected_fingerprint) session.mount('https://critical-api.com', adapter) response = session.get( 'https://critical-api.com/sensitive-data', timeout=10 ) response.raise_for_status() return response.json() except requests.exceptions.SSLError as e: return {'error': f'Certificate pinning failed: {str(e)}'}, 400 except requests.exceptions.RequestException as e: return {'error': str(e)}, 500 # Configuration for internal CA @app.route('/internal_ca_example') def internal_ca_example(): """Example with custom CA bundle for internal services.""" try: # Use custom CA bundle for internal certificates ca_bundle_path = '/etc/ssl/certs/internal-ca-bundle.pem' response = requests.get( 'https://internal-service.company.com/api', verify=ca_bundle_path, # Custom CA bundle timeout=10 ) response.raise_for_status() return response.json() except requests.exceptions.SSLError as e: return {'error': f'Internal SSL verification failed: {str(e)}'}, 400 except requests.exceptions.RequestException as e: return {'error': str(e)}, 500

💡 Why This Fix Works

See fix suggestions for detailed explanation.

Why it happens

Code disables certificate verification: requests.get(url, verify=False). Bypasses SSL/TLS certificate validation. Accepts any certificate including self-signed. Enables man-in-the-middle attacks. Attackers intercept and modify HTTPS traffic. Completely negates HTTPS security. Common during debugging, accidentally left in production.

Root causes

Using requests with verify=False

Code disables certificate verification: requests.get(url, verify=False). Bypasses SSL/TLS certificate validation. Accepts any certificate including self-signed. Enables man-in-the-middle attacks. Attackers intercept and modify HTTPS traffic. Completely negates HTTPS security. Common during debugging, accidentally left in production.

Disabling Warnings for Unverified HTTPS

Suppressing verification warnings: import urllib3; urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning). Hides security alerts. verify=False silently accepted. Warnings exist for security. Disabling warnings masks vulnerabilities. Production code should never suppress security warnings.

Using verify=False to Work Around Certificate Issues

Certificate errors bypassed instead of fixed. Self-signed certificates in development. Expired certificates. Hostname mismatches. verify=False quick fix avoiding proper certificate management. Root cause not addressed. Temporary debugging solution becoming permanent.

Environment Variables Controlling Certificate Verification

Configuration allowing disabled verification: VERIFY_SSL = os.environ.get('VERIFY_SSL', 'false'); requests.get(url, verify=VERIFY_SSL=='true'). Environment controls security setting. Production misconfiguration disables verification. Defaults should be secure. No option to disable should exist in production code.

Not Understanding SSL/TLS Certificate Validation Purpose

Developers unaware of certificate validation importance. Believing HTTPS alone provides security. Not understanding man-in-the-middle attacks. Certificate validation essential for authentication. Encryption without authentication provides no security against active attackers.

Fixes

1

Never Use verify=False in Production Code

Always verify certificates: requests.get(url, verify=True) or omit verify (defaults to True). Never disable verification in production. Remove all verify=False from codebase. Treat certificate errors as bugs to fix, not ignore. Certificate validation non-negotiable for HTTPS security.

2

Provide CA Bundle for Self-Signed Certificates

For self-signed certs, use CA bundle: requests.get(url, verify='/path/to/ca-bundle.crt'). Add self-signed CA to bundle. Maintain internal CA certificate. Install CA cert in system trust store. Proper certificate management instead of disabling validation.

3

Use certifi for Updated CA Certificates

Use certifi library: import certifi; requests.get(url, verify=certifi.where()). Provides updated CA bundle. Independent of system certificates. Regular updates for new CAs and revocations. Better than system certificates on some platforms. requests uses certifi by default.

4

Implement Certificate Pinning for Critical Services

Pin certificates for sensitive APIs: requests.get(url, verify='/path/to/server-cert.pem'). Specific certificate or CA. Prevents man-in-the-middle with compromised CAs. Critical for authentication, payments, sensitive data. Defense-in-depth with pinning. Higher security than standard validation.

5

Fix Certificate Errors Properly, Never Work Around

Address root causes: renew expired certificates; fix hostname mismatches; install proper CA certificates. Certificate errors indicate real problems. Debugging with verify=False acceptable temporarily but must be fixed before commit. Code review rejecting verify=False.

6

Scan for verify=False and Remove All Instances

Find insecure usage: grep -r 'verify=False' --include="*.py". Use bandit security scanner. Pre-commit hooks rejecting verify=False. CI/CD failures on disabled verification. Code review checklist. Complete elimination from codebase.

Detect This Vulnerability in Your Code

Sourcery automatically identifies python requests disabled certificate validation vulnerability and many other security issues in your codebase.