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.