Python Requests Authentication over HTTP Vulnerability

High Risk Insecure Transport
PythonRequestsHTTPAuthenticationCredentialsTransport Security

What it is

Application sends authentication credentials over unencrypted HTTP connections, exposing sensitive authentication data to network eavesdropping and interception.

import requests
from flask import Flask, request

@app.route('/api_request')
def api_request():
    # Vulnerable: API key sent over HTTP
    api_key = 'sk-1234567890abcdef'
    url = 'http://api.service.com/data'  # HTTP, not HTTPS
    
    headers = {
        'Authorization': f'Bearer {api_key}'  # Credentials over HTTP
    }
    
    response = requests.get(url, headers=headers)
    return response.json()

@app.route('/login', methods=['POST'])
def login():
    # Vulnerable: Basic auth over HTTP
    username = request.form.get('username')
    password = request.form.get('password')
    
    auth_url = 'http://auth.service.com/login'  # Insecure
    
    # Credentials transmitted in plaintext
    response = requests.post(
        auth_url,
        auth=(username, password),  # Basic auth over HTTP
        data={'action': 'login'}
    )
    
    return response.json()

@app.route('/oauth_callback')
def oauth_callback():
    # Vulnerable: OAuth token over HTTP
    access_token = request.args.get('access_token')
    
    # Token sent to HTTP endpoint
    profile_url = 'http://api.social.com/me'
    headers = {'Authorization': f'Bearer {access_token}'}
    
    response = requests.get(profile_url, headers=headers)
    return response.json()
import requests
from urllib.parse import urlparse
from flask import Flask, request
import os

def validate_https_url(url):
    """Ensure URL uses HTTPS scheme."""
    parsed = urlparse(url)
    if parsed.scheme != 'https':
        raise ValueError(f'Only HTTPS URLs allowed for authenticated requests, got: {parsed.scheme}')
    return url

def create_secure_auth_headers(token, token_type='Bearer'):
    """Create secure authentication headers."""
    if not token:
        raise ValueError('Token cannot be empty')
    
    return {
        'Authorization': f'{token_type} {token}',
        'User-Agent': 'SecureApp/1.0',
        'Accept': 'application/json'
    }

@app.route('/api_request')
def api_request():
    """Secure API request with HTTPS validation."""
    try:
        # Secure: API key from environment
        api_key = os.getenv('API_KEY')
        if not api_key:
            return {'error': 'API key not configured'}, 500
        
        # Secure: HTTPS URL
        url = 'https://api.service.com/data'
        validate_https_url(url)
        
        # Secure headers
        headers = create_secure_auth_headers(api_key)
        
        response = requests.get(
            url,
            headers=headers,
            timeout=10,
            verify=True  # Ensure certificate validation
        )
        response.raise_for_status()
        
        return response.json()
        
    except ValueError as e:
        return {'error': str(e)}, 400
    except requests.exceptions.RequestException as e:
        return {'error': f'API request failed: {str(e)}'}, 500

@app.route('/login', methods=['POST'])
def login():
    """Secure login with HTTPS enforcement."""
    try:
        username = request.form.get('username', '')
        password = request.form.get('password', '')
        
        if not username or not password:
            return {'error': 'Username and password required'}, 400
        
        # Secure: HTTPS authentication endpoint
        auth_url = 'https://auth.service.com/login'
        validate_https_url(auth_url)
        
        # Secure: Basic auth over HTTPS only
        response = requests.post(
            auth_url,
            auth=(username, password),
            data={'action': 'login'},
            timeout=15,
            verify=True,
            headers={
                'Content-Type': 'application/x-www-form-urlencoded',
                'User-Agent': 'SecureApp/1.0'
            }
        )
        response.raise_for_status()
        
        return response.json()
        
    except ValueError as e:
        return {'error': str(e)}, 400
    except requests.exceptions.RequestException as e:
        return {'error': f'Authentication failed: {str(e)}'}, 500

@app.route('/oauth_callback')
def oauth_callback():
    """Secure OAuth callback with token validation."""
    try:
        access_token = request.args.get('access_token', '')
        
        if not access_token:
            return {'error': 'Access token required'}, 400
        
        # Validate token format (basic check)
        if len(access_token) < 20 or not access_token.replace('-', '').replace('_', '').isalnum():
            return {'error': 'Invalid token format'}, 400
        
        # Secure: HTTPS profile endpoint
        profile_url = 'https://api.social.com/me'
        validate_https_url(profile_url)
        
        headers = create_secure_auth_headers(access_token)
        
        response = requests.get(
            profile_url,
            headers=headers,
            timeout=10,
            verify=True
        )
        response.raise_for_status()
        
        return response.json()
        
    except ValueError as e:
        return {'error': str(e)}, 400
    except requests.exceptions.RequestException as e:
        return {'error': f'Profile request failed: {str(e)}'}, 500

# Secure session configuration
class SecureAuthSession(requests.Session):
    """Session that enforces HTTPS for authenticated requests."""
    
    def request(self, method, url, **kwargs):
        # Check if request includes authentication
        has_auth = (
            'Authorization' in kwargs.get('headers', {}) or
            'auth' in kwargs or
            'cookies' in kwargs
        )
        
        if has_auth:
            validate_https_url(url)
        
        # Ensure certificate validation
        kwargs.setdefault('verify', True)
        kwargs.setdefault('timeout', 10)
        
        return super().request(method, url, **kwargs)

@app.route('/secure_session_example')
def secure_session_example():
    """Example using secure session."""
    try:
        session = SecureAuthSession()
        
        # This will enforce HTTPS due to Authorization header
        response = session.get(
            'https://api.example.com/protected',
            headers={'Authorization': 'Bearer token123'}
        )
        
        return {'status': response.status_code}
        
    except ValueError as e:
        return {'error': str(e)}, 400
    except requests.exceptions.RequestException as e:
        return {'error': str(e)}, 500

# Environment-based configuration
@app.route('/config_example')
def config_example():
    """Example with environment-based secure configuration."""
    # Load configuration from environment
    api_base_url = os.getenv('API_BASE_URL', 'https://api.example.com')
    api_key = os.getenv('API_KEY')
    
    if not api_key:
        return {'error': 'API not configured'}, 500
    
    try:
        validate_https_url(api_base_url)
        
        url = f'{api_base_url}/secure-endpoint'
        headers = create_secure_auth_headers(api_key)
        
        response = requests.get(url, headers=headers, timeout=10)
        response.raise_for_status()
        
        return response.json()
        
    except ValueError as e:
        return {'error': 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 sends auth with HTTP: requests.get('http://api.example.com', auth=('user', 'pass')). HTTP transmits credentials unencrypted. Basic auth sends base64-encoded credentials (easily decoded). Digest auth vulnerable to eavesdropping over HTTP. Network attackers capture credentials. Authentication over HTTP provides no security.

Root causes

Sending Authentication Credentials Over HTTP URLs

Code sends auth with HTTP: requests.get('http://api.example.com', auth=('user', 'pass')). HTTP transmits credentials unencrypted. Basic auth sends base64-encoded credentials (easily decoded). Digest auth vulnerable to eavesdropping over HTTP. Network attackers capture credentials. Authentication over HTTP provides no security.

Using Authorization Headers with HTTP Connections

Headers with tokens over HTTP: headers = {'Authorization': f'Bearer {token}'}; requests.get('http://api.com', headers=headers). API keys, JWTs, OAuth tokens transmitted in clear. Token interception enables account takeover. Headers visible to network observers. HTTP + authorization creates credential exposure.

HTTP APIs with Cookie-Based Authentication

Session cookies over HTTP: requests.get('http://app.com', cookies={'session_id': session}). Session cookies without Secure flag. Authentication cookies transmitted unencrypted. Session hijacking trivial with HTTP. Mixed HTTP/HTTPS usage leaks session tokens.

Not Validating URL Scheme Before Adding Authentication

Adding auth without protocol check: requests.get(url, auth=credentials). URL from configuration or user input may be HTTP. No validation ensuring HTTPS. Applications trust URL source without verification. Credentials accidentally sent over HTTP through URL mistakes.

Development or Testing with HTTP Authentication

Debug/test code using HTTP: if DEBUG: api_url = 'http://localhost'; requests.get(api_url, auth=auth). Development patterns with HTTP. Debug configurations in production builds. Environment variable failures activating HTTP. Development practice creating security vulnerabilities.

Fixes

1

Never Send Authentication Over HTTP, Always Use HTTPS

Use HTTPS for authenticated requests: requests.get('https://api.example.com', auth=credentials). All authentication requires HTTPS. Basic, Digest, Bearer tokens, API keys all need encryption. HTTPS mandatory for any auth mechanism. No exceptions for development or internal services.

2

Validate URL Scheme Before Adding Authentication

Check HTTPS before auth: from urllib.parse import urlparse; if urlparse(url).scheme != 'https': raise ValueError('HTTPS required for authentication'); requests.get(url, auth=credentials). Validation prevents accidental HTTP auth. Fail fast on protocol violations.

3

Use Secure Session Cookies with HTTPS Only

Configure secure cookies: session.cookies.set('auth_token', token, secure=True, httponly=True, samesite='Strict'). Secure flag prevents transmission over HTTP. HttpOnly prevents JavaScript access. SameSite prevents CSRF. Cookies with auth require all security flags.

4

Implement Request Wrapper Enforcing HTTPS for Auth

Wrapper function validating security: def authenticated_request(url, **kwargs): if 'auth' in kwargs and not url.startswith('https://'): raise ValueError('Auth requires HTTPS'); return requests.get(url, **kwargs). Centralized enforcement. Prevents scattered HTTP auth. Application-wide protection.

5

Use mTLS for Service-to-Service Authentication

Mutual TLS for APIs: cert = ('/path/to/client.cert', '/path/to/client.key'); requests.get('https://api.com', cert=cert, verify='/path/to/ca.crt'). Client certificate authentication. Mutual authentication. No passwords over network. Modern service mesh authentication.

6

Scan for HTTP URLs with Authentication Headers

Find insecure auth: grep -rE 'http://.*auth=' --include="*.py" or grep -r "'http://" --include="*.py" | grep -i auth. Static analysis for auth over HTTP. Code review for authentication code. Ban HTTP in authenticated endpoints. Complete elimination.

Detect This Vulnerability in Your Code

Sourcery automatically identifies python requests authentication over http vulnerability and many other security issues in your codebase.