Missing Tls in Flask
How Missing TLS Manifests in Flask
Missing TLS in Flask applications creates several critical attack vectors that directly impact API security. When Flask APIs communicate over HTTP instead of HTTPS, attackers can intercept credentials, tokens, and sensitive data in transit through man-in-the-middle attacks.
The most dangerous manifestation occurs during authentication flows. Consider a Flask endpoint handling user login:
from flask import Flask, request, jsonify
app = Flask(__name__)
@app.route('/login', methods=['POST'])
def login():
data = request.json
username = data['username']
password = data['password'] # Transmitted in plaintext!
# Authentication logic here
return jsonify({'token': 'user_token'})
When this endpoint is accessed over HTTP, an attacker on the same network can capture the username and password using tools like Wireshark or tcpdump. The authentication token returned in the response is also exposed, allowing session hijacking.
Another critical scenario involves API keys and tokens in request headers. Many Flask applications implement token-based authentication:
@app.route('/api/data', methods=['GET'])
def get_data():
auth_header = request.headers.get('Authorization')
if not auth_header or not auth_header.startswith('Bearer '):
return jsonify({'error': 'Missing token'}), 401
token = auth_header.split(' ')[1] # Token transmitted in plaintext!
# Process request
return jsonify({'data': 'sensitive information'})
Without TLS, these Bearer tokens are trivially intercepted. Attackers can then use the stolen tokens to impersonate legitimate users, leading to complete account takeover.
Flask applications often serve as backends for mobile apps or single-page applications. When these clients communicate with Flask APIs over unsecured HTTP, attackers can modify responses in transit. For example, an API returning financial data could have its responses altered to show incorrect balances or transfer funds to attacker-controlled accounts.
Environment variables containing database credentials, API keys, or other secrets are also at risk. Flask applications frequently load these from configuration files or environment variables, and without TLS, configuration files transmitted during deployment or updates can be intercepted.
Another Flask-specific issue arises with debug mode. When Flask runs with debug=True, it enables an interactive debugger that can execute arbitrary Python code. If this debug endpoint is exposed over HTTP, attackers can remotely execute code on the server:
if __name__ == '__main__':
app.run(debug=True, host='0.0.0.0') # Dangerous in production!
The interactive debugger's traceback pages, when served over HTTP, can reveal source code, configuration details, and internal logic that aids attackers in crafting more sophisticated attacks.
Flask-Specific Detection
Detecting missing TLS in Flask applications requires examining both the application configuration and runtime behavior. The most direct approach is checking the Flask server configuration:
from flask import Flask
app = Flask(__name__)
# Check if running in debug mode
if app.config.get('DEBUG'):
print('Warning: Debug mode enabled - do not use in production')
# Check if running on HTTP
if app.config.get('PREFERRED_URL_SCHEME') == 'http':
print('Warning: Application configured to use HTTP')
However, this only checks the application's preferences. The actual protocol used depends on how the application is deployed. When using Flask's built-in development server, you can inspect the run configuration:
if __name__ == '__main__':
# Dangerous - running without TLS
app.run(host='0.0.0.0', port=5000)
# Safe - requires external TLS termination
app.run(host='0.0.0.0', port=5000, ssl_context='adhoc')
For production deployments using WSGI servers like Gunicorn or uWSGI, the TLS configuration is external to Flask. You need to examine the deployment configuration:
# Gunicorn with SSL
command = ['gunicorn',
'--certfile', 'server.crt',
'--keyfile', 'server.key',
'--bind', '0.0.0.0:443',
'app:app']
Automated scanning tools like middleBrick can detect missing TLS by attempting to access API endpoints over HTTPS and verifying certificate validity. The scanner tests the unauthenticated attack surface and reports whether endpoints are accessible over unsecured HTTP:
$ middlebrick scan https://api.example.com
=== Security Scan Results ===
TLS Status: FAIL
- Endpoint accessible over HTTP
- No valid SSL certificate found
- Recommendation: Enable HTTPS with valid certificate
middleBrick performs 12 parallel security checks, including TLS verification, and provides a security score from A to F. For Flask applications, it specifically identifies endpoints that accept HTTP connections and provides remediation guidance based on OWASP API Security Top 10 standards.
Another detection method involves checking for mixed content in responses. Flask applications serving HTML or JavaScript that make API calls can inadvertently expose data through insecure connections:
@app.route('/')
def index():
return render_template('index.html') # Contains HTTP API calls!
Tools like OWASP ZAP or Burp Suite can intercept traffic and identify unencrypted API calls, even when the main application runs over HTTPS.
Flask-Specific Remediation
Remediating missing TLS in Flask applications requires both application-level changes and deployment configuration. The most straightforward approach is using Flask's built-in SSL support for development:
from flask import Flask
app = Flask(__name__)
if __name__ == '__main__':
# Generate self-signed certificate for development
app.run(host='0.0.0.0', port=5000,
ssl_context=('cert.pem', 'key.pem'))
For production, Flask should never handle TLS directly. Instead, use a production WSGI server with proper TLS termination:
# gunicorn.conf.py
workers = 4
bind = '0.0.0.0:8000'
# TLS handled by reverse proxy (nginx, apache, etc.)
Configure a reverse proxy like Nginx to handle TLS termination:
server {
listen 80;
server_name api.example.com;
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2;
server_name api.example.com;
ssl_certificate /etc/ssl/certs/api.crt;
ssl_certificate_key /etc/ssl/private/api.key;
location / {
proxy_pass http://localhost:8000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
In your Flask application, ensure you respect the forwarded protocol:
from flask import Flask, request
app = Flask(__name__)
app.config['PREFERRED_URL_SCHEME'] = 'https'
@app.before_request
def enforce_https():
if request.headers.get('X-Forwarded-Proto') == 'http':
url = request.url.replace('http://', 'https://', 1)
return redirect(url, code=301)
For Flask applications using Blueprints or extensions, ensure all API endpoints enforce HTTPS. Create a decorator for critical endpoints:
from functools import wraps
def require_https(f):
@wraps(f)
def decorated_function(*args, **kwargs):
if request.headers.get('X-Forwarded-Proto') == 'http':
return jsonify({'error': 'HTTPS required'}), 400
return f(*args, **kwargs)
return decorated_function
@app.route('/api/secure', methods=['POST'])
@require_https
def secure_endpoint():
data = request.json
# Process secure data
return jsonify({'status': 'success'})
middleBrick's CLI tool can verify your remediation by scanning the secured endpoint:
$ middlebrick scan https://api.example.com --fail-below B
=== Security Scan Results ===
TLS Status: PASS
- All endpoints require HTTPS
- Valid SSL certificate detected
- Security Score: A
Integrate middleBrick into your CI/CD pipeline to prevent regressions:
# .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-api.example.com --fail-below B
This configuration ensures that any deployment with missing TLS will fail the build, preventing insecure code from reaching production. The continuous monitoring in middleBrick's Pro plan can also alert you if TLS certificates expire or configurations change unexpectedly.
Related CWEs: encryption
| CWE ID | Name | Severity |
|---|---|---|
| CWE-319 | Cleartext Transmission of Sensitive Information | HIGH |
| CWE-295 | Improper Certificate Validation | HIGH |
| CWE-326 | Inadequate Encryption Strength | HIGH |
| CWE-327 | Use of a Broken or Risky Cryptographic Algorithm | HIGH |
| CWE-328 | Use of Weak Hash | HIGH |
| CWE-330 | Use of Insufficiently Random Values | HIGH |
| CWE-338 | Use of Cryptographically Weak PRNG | MEDIUM |
| CWE-693 | Protection Mechanism Failure | MEDIUM |
| CWE-757 | Selection of Less-Secure Algorithm During Negotiation | HIGH |
| CWE-261 | Weak Encoding for Password | HIGH |