HIGH auth bypassflaskbasic auth

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: Basic header 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 IDNameSeverity
CWE-287Improper Authentication CRITICAL
CWE-306Missing Authentication for Critical Function CRITICAL
CWE-307Brute Force HIGH
CWE-308Single-Factor Authentication MEDIUM
CWE-309Use of Password System for Primary Authentication MEDIUM
CWE-347Improper Verification of Cryptographic Signature HIGH
CWE-384Session Fixation HIGH
CWE-521Weak Password Requirements MEDIUM
CWE-613Insufficient Session Expiration MEDIUM
CWE-640Weak Password Recovery HIGH

Frequently Asked Questions

Can Basic Auth be safely used without TLS in a Flask app?
No. Basic Auth encodes credentials but does not encrypt them. Without TLS, credentials are transmitted in base64 and can be decoded by any intermediary. Always enforce TLS to protect credentials in transit.
How can I test whether my Flask routes are vulnerable to auth bypass using middleBrick?
Run a scan with middleBrick against your staging URL. It performs unauthenticated checks across authentication, data exposure, and input validation categories, highlighting endpoints that accept requests without valid Authorization headers or that leak credentials.