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=Truein 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?
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?
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.