Broken Authentication in Flask with Basic Auth
Broken Authentication in Flask with Basic Auth — how this specific combination creates or exposes the vulnerability
Using HTTP Basic Authentication in Flask without additional protections is a common source of Broken Authentication issues. Basic Auth sends credentials in an Authorization header that is base64-encoded but not encrypted, so any party on the network path that can observe the request can decode the credentials. In Flask, developers sometimes implement Basic Auth manually with decorators and helpers, and if they skip transport-layer protections or reuse the same credentials across environments, the attack surface expands.
One typical pattern that leads to vulnerability is a custom check_auth function that validates a hardcoded username and password without rate limiting or secure credential storage. For example:
from flask import Flask, request, Response
import base64
app = Flask(__name__)
def check_auth(username, password):
return username == 'admin' and password == 'secret123'
def authenticate():
return Response(
'Could not verify your access level for URL: %s' % request.url,
401,
{'WWW-Authenticate': 'Basic realm="Login Required"'}
)
@app.route('/admin')
def admin_page():
auth = request.authorization
if not auth or not check_auth(auth.username, auth.password):
return authenticate()
return 'Admin Panel: Top secret data'
if __name__ == '__main__':
app.run(debug=True)
This example highlights multiple risk factors: the credentials are hardcoded and compared in plaintext, debugging is enabled for the Flask app, and there is no enforcement of HTTPS. When Basic Auth is used over HTTP, credentials are trivial to intercept. Even over HTTPS, additional concerns remain, such as the lack of account lockout after repeated failures, which makes brute-force attacks feasible. Because middleBrick scans the unauthenticated attack surface, it can detect endpoints that expose admin routes with Basic Auth and flag weak or missing protections as a Broken Authentication finding.
Additionally, when the same credentials are reused across multiple environments (development, staging, production), a leaked credential from a less-protected environment can lead to unauthorized access in production. Without multi-factor authentication or session management, Basic Auth provides no mechanism for revocation beyond changing the hardcoded password. This aligns with the Broken Authentication category in the OWASP API Top 10 and can result in sensitive data exposure or privilege escalation.
middleBrick’s 12 security checks run in parallel and can identify endpoints using Basic Auth over unencrypted channels, missing rate limiting, and weak authorization boundaries. The scanner correlates these findings with the OpenAPI spec when available, helping teams understand how authentication mechanisms map to declared routes and operations.
Basic Auth-Specific Remediation in Flask — concrete code fixes
To address Broken Authentication when using Basic Auth in Flask, you should enforce HTTPS, avoid hardcoded credentials, and integrate with secure credential stores. Below is a revised implementation that mitigates the most common risks.
First, always require HTTPS in production. You can enforce this in Flask using a before-request hook and a trusted proxy setup at the load balancer or reverse proxy:
from flask import Flask, request, Response
from functools import wraps
import os
app = Flask(__name__)
def require_https(f):
@wraps(f)
def decorated(*args, **kwargs):
if not request.is_secure:
return 'HTTPS required', 403
return f(*args, **kwargs)
return decorated
app.before_request(require_https)
Second, use environment variables or a secrets manager instead of hardcoding credentials. For example, load credentials from environment variables and compare them securely:
import os
from werkzeug.security import generate_password_hash, check_password_hash
ADMIN_USER = os.environ.get('ADMIN_USER')
ADMIN_PASS_HASH = os.environ.get('ADMIN_PASS_HASH')
def check_auth(username, password):
if not ADMIN_USER or not ADMIN_PASS_HASH:
return False
return username == ADMIN_USER and check_password_hash(ADMIN_PASS_HASH, password)
Generate the hash once with Python and store it in your environment:
from werkzeug.security import generate_password_hash
print(generate_password_hash('your_strong_password'))
Third, add rate limiting to prevent brute-force attacks. You can use Flask-Limiter to restrict the number of authentication attempts per IP or user:
from flask_limiter import Limiter
from flask_limiter.util import get_remote_address
limiter = Limiter(key_func=get_remote_address, default_limits=["5 per minute"])
app = Flask(__name__)
limiter.init_app(app)
@app.route('/admin')
@limiter.limit("5 per minute")
def admin_page():
auth = request.authorization
if not auth or not check_auth(auth.username, auth.password):
return authenticate()
return 'Admin Panel: Top secret data'
For production deployments, consider using an external authentication provider or API gateway to manage credentials and TLS termination. middleBrick’s scans can validate that HTTPS is enforced and that authentication endpoints include proper rate limiting and secure credential handling. With the Pro plan, you can enable continuous monitoring so that any regression in authentication controls is flagged promptly, and the GitHub Action can fail builds if a new route exposes Basic Auth without HTTPS.
Related CWEs: authentication
| CWE ID | Name | Severity |
|---|---|---|
| CWE-287 | Improper Authentication | CRITICAL |
| CWE-306 | Missing Authentication for Critical Function | CRITICAL |
| CWE-307 | Brute Force | HIGH |
| CWE-308 | Single-Factor Authentication | MEDIUM |
| CWE-309 | Use of Password System for Primary Authentication | MEDIUM |
| CWE-347 | Improper Verification of Cryptographic Signature | HIGH |
| CWE-384 | Session Fixation | HIGH |
| CWE-521 | Weak Password Requirements | MEDIUM |
| CWE-613 | Insufficient Session Expiration | MEDIUM |
| CWE-640 | Weak Password Recovery | HIGH |