Auth Bypass in Flask with Basic Auth
Auth Bypass in Flask with Basic Auth — how this specific combination creates or exposes the vulnerability
Using HTTP Basic Authentication in Flask can create an auth bypass when developers apply incomplete checks or rely on client-side enforcement. A common pattern is to decode the Authorization header, verify credentials, and then call flask.request.authorization without enforcing the check on every sensitive route. If a route omits the verification step or conditionally skips it based on environment flags, an authenticated context established once can be reused across requests, effectively bypassing the intended protection.
Basic Auth transmits credentials in base64-encoded form, which is easily reversible if not protected by TLS. In Flask, if TLS is not enforced or is misconfigured (for example, terminating TLS at a load balancer without forwarding the original scheme), an attacker on the network can observe or modify the credentials. Even when TLS is used, storing or logging the decoded credentials in server-side logs or error messages can lead to accidental exposure, enabling an attacker to reuse them in unauthenticated scans by middleBrick to discover endpoints that incorrectly allow access without revalidating credentials.
Another bypass vector arises from how Flask handles session and request context. Developers sometimes use before_request to set a global flag like g.authenticated after a successful check, but forget to reset it on logout or on requests that should remain public. Routes that rely on this flag instead of re-parsing and validating the Authorization header on each call can be accessed without valid credentials when the flag remains true from a prior authenticated request. MiddleBrick’s unauthenticated scan can surface these routes by probing endpoints that should require authentication but do not validate the header on every call, revealing BOLA/IDOR-like access patterns where one user’s context grants access to another’s data.
Flask’s built-in auth.login_required decorator from extensions like Flask-Login or custom wrappers can also be bypassed if they check only the presence of a session cookie instead of the current request’s Authorization header. When Basic Auth is used, the credentials must be verified per request from the header, not from a separate session store. If the verification logic is inconsistent across endpoints or skipped for methods like OPTIONS or preflight CORS requests, an attacker can leverage those paths to reach protected resources, a pattern often highlighted in the authentication checks run by middleBrick.
Finally, misconfigured CORS or route exposure can compound the issue. A route that returns sensitive data and relies on Basic Auth may still respond to cross-origin requests if CORS headers are too permissive, allowing an authenticated response to be read by a malicious site. middleBrick tests input validation and data exposure across origins, which can highlight cases where authentication controls are undermined by relaxed CORS policies, enabling unauthorized data retrieval despite the presence of Basic Auth headers.
Basic Auth-Specific Remediation in Flask — concrete code fixes
Remediation centers on strict per-request validation, avoiding global flags for authentication state, and enforcing TLS. Always decode and verify the Authorization header on every protected route, and ensure responses do not leak credentials.
Secure Basic Auth implementation example
import base64
from flask import Flask, request, Response, g
app = Flask(__name__)
VALID_USERS = {
"admin": "5f4dcc3b5aa765d61d8327deb882cf99", # MD5 of "password"
}
def verify_auth(auth_header):
if not auth_header or not auth_header.startswith("Basic "):
return None
try:
encoded = auth_header.split(" ")[1]
decoded = base64.b64decode(encoded).decode("utf-8")
username, password = decoded.split(":", 1)
if VALID_USERS.get(username) == password:
return username
except Exception:
return None
return None
@app.before_request
def authenticate():
# Exclude public endpoints explicitly
if request.endpoint in ("login", "static"):
return
username = verify_auth(request.headers.get("Authorization"))
if username is None:
return Response(
"Could not verify your access level for that URL.\n"
"You have to login with proper credentials",
401,
{"WWW-Authenticate": "Basic realm=\"Login Required\""},
)
g.user = username
@app.route("/protected")
def protected():
return f"Hello {g.user}, this is protected."
@app.route("/public")
def public():
return "Public data."
if __name__ == "__main__":
app.run(ssl_context="adhoc") # Use proper TLS in production
Key practices derived from this pattern:
- Verify credentials on every request using
request.headers, not session state. - Use HTTP 401 with a
WWW-Authenticate: Basicheader for challenges, avoiding 403 when authentication is missing. - Never log or store decoded credentials; treat the header as opaque after verification.
- Enforce TLS in production (e.g., via a proper certificate) to prevent credential leakage; ad hoc TLS is for testing only.
- Explicitly exclude public routes from authentication logic to avoid unnecessary overhead and potential misconfiguration.
When using extensions, ensure they validate the header directly rather than relying on cookie-based sessions. middleBrick’s authentication checks can validate these implementations by scanning unauthenticated attack surfaces and confirming that protected endpoints reject requests without valid Authorization headers, reducing the risk of auth bypass due to inconsistent checks.
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 |