HIGH api key exposureflask

Api Key Exposure in Flask

How Api Key Exposure Manifests in Flask

Api Key Exposure in Flask applications typically occurs through several Flask-specific patterns that developers often overlook. The most common manifestation is hardcoded API keys in application code. Flask developers frequently embed credentials directly in their app.py or config.py files:

from flask import Flask
app = Flask(__name__)

# Hardcoded API keys - critical security flaw
API_KEY = "sk-1234567890abcdef"
SECRET_KEY = "supersecretkey"

Another Flask-specific vulnerability occurs with debug mode enabled in production. When app.run(debug=True) is active, Flask's interactive debugger can expose environment variables and configuration data through the Werkzeug debugger interface:

if __name__ == '__main__':
    app.run(debug=True)  # NEVER do this in production

Flask's configuration system can also lead to exposure when developers use app.config.from_object() or app.config.from_pyfile() with files that contain sensitive data. The configuration becomes accessible through app.config which can be inadvertently exposed through error handlers or debug endpoints.

Environment variable exposure is particularly problematic in Flask applications deployed on platforms like Heroku or AWS Elastic Beanstalk. Developers often use os.environ.get() to retrieve keys but fail to validate their presence, leading to None values that propagate through the application and potentially appear in error responses.

Flask's error handling can also leak API keys. When exceptions occur, Flask's default error pages or custom error handlers might inadvertently include stack traces containing sensitive configuration data. This is especially dangerous when combined with Flask's debug mode or when error details are sent to client applications.

Flask-Specific Detection

Detecting API key exposure in Flask applications requires both static code analysis and runtime scanning. For static analysis, you can use tools like bandit or custom scripts to search for common patterns:

import ast
import re

def find_api_keys_in_flask_code(filepath):
    """Scan Flask application for potential API key exposure"""
    api_key_patterns = [
        r'API_KEY\s*=\s*["\'][^"\']+["\']',
        r'SECRET_KEY\s*=\s*["\'][^"\']+["\']',
        r'os\.environ\.get\([^)]*\)'
    ]
    
    with open(filepath, 'r') as f:
        content = f.read()
    
    findings = []
    for pattern in api_key_patterns:
        for match in re.finditer(pattern, content):
            findings.append({
                'line': content[:match.start()].count('\n') + 1,
                'code': match.group(),
                'severity': 'HIGH'
            })
    
    return findings

For runtime detection, middleBrick provides specialized scanning for Flask applications. The scanner identifies Flask-specific endpoints and examines their behavior for key exposure:

# Scan a Flask API endpoint
middlebrick scan https://api.example.com --output json

middleBrick's Flask-specific checks include:

  • Debug mode detection: Identifies debug=True in Flask configurations
  • Configuration exposure: Scans for endpoints that might return app.config data
  • Environment variable leakage: Tests for unintended exposure of environment variables
  • Stack trace analysis: Detects whether error responses contain sensitive data

The scanner also examines Flask's error handling mechanisms and debug endpoints that might be accidentally left enabled in production environments. This is particularly important because Flask's debug mode can provide full application introspection to attackers.

Flask-Specific Remediation

Securing API keys in Flask applications requires a multi-layered approach using Flask's built-in features and best practices. The first step is proper configuration management using Flask's configuration system with environment variables:

import os
from flask import Flask

app = Flask(__name__)

# Load configuration from environment variables
app.config.from_mapping(
    SECRET_KEY=os.environ.get('SECRET_KEY'),
    API_KEY=os.environ.get('API_KEY'),
    DEBUG=bool(os.environ.get('DEBUG', 'False'))
)

# Validate required configuration
required_config = ['SECRET_KEY', 'API_KEY']
for config_key in required_config:
    if not app.config.get(config_key):
        raise RuntimeError(f'Missing required configuration: {config_key}')

For production deployments, use Flask's configuration files with proper environment separation:

# config/production.py
class ProductionConfig:
    SECRET_KEY = os.environ['SECRET_KEY']
    API_KEY = os.environ['API_KEY']
    DEBUG = False
    TESTING = False

# config/development.py
class DevelopmentConfig:
    SECRET_KEY = 'dev-secret-key'
    API_KEY = 'dev-api-key'
    DEBUG = True
    TESTING = True

# app.py
app.config.from_object('config.ProductionConfig')

Implement proper error handling to prevent key exposure:

from flask import jsonify

@app.errorhandler(500)
def internal_error(error):
    """Custom error handler that doesn't expose sensitive data"""
    app.logger.error('Internal Server Error: %s', error)
    return jsonify({
        'error': 'Internal Server Error',
        'message': 'An unexpected error occurred'
    }), 500

# Disable debug mode in production
if app.config.get('DEBUG'):
    @app.before_request
def check_debug_mode():
        if not app.config.get('TESTING'):
            raise RuntimeError('Debug mode must be disabled in production')

Use Flask's built-in security features for key management:

from flask import request, abort

@app.route('/api/protected')
def protected_endpoint():
    """Validate API key in headers"""
    api_key = request.headers.get('X-API-Key')
    if api_key != app.config['API_KEY']:
        abort(401, description='Invalid API key')
    
    return jsonify({'message': 'Access granted'})

For comprehensive security, integrate middleBrick's continuous monitoring into your Flask deployment pipeline:

# .github/workflows/security.yml
name: Security Scan
on: [push, pull_request]

jobs:
  security:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - name: Run middleBrick scan
        run: |
          npm install -g middlebrick
          middlebrick scan https://staging.example.com/api --fail-below B

This approach ensures API keys remain protected throughout the development lifecycle, from local development through production deployment.

Frequently Asked Questions

How can I test if my Flask API key is exposed without using middleBrick?
You can perform manual testing by checking your Flask configuration files for hardcoded keys, verifying debug mode is disabled in production, and testing your endpoints with tools like curl or Postman to see if sensitive data appears in error responses. Look specifically for debug=True in your configuration and check if app.config data is accessible through any endpoints.
What's the difference between API key exposure and secret key exposure in Flask?
API key exposure typically refers to keys used for external service authentication (like Stripe, Twilio, or third-party APIs), while secret keys in Flask usually refer to the application's SECRET_KEY used for session signing and CSRF protection. Both are critical, but API keys often have broader access implications since they can authenticate to external services, potentially leading to data exfiltration or service abuse.