HIGH header injectionflaskbasic auth

Header Injection in Flask with Basic Auth

Header Injection in Flask with Basic Auth — how this specific combination creates or exposes the vulnerability

Header Injection in Flask when Basic Authentication is used occurs when untrusted input from a client is reflected into HTTP response headers without validation or encoding. Basic Auth typically relies on the Authorization header, but Flask applications often build custom headers (e.g., X-User, X-Trace-Id) from request data, query parameters, or parsed tokens. If these values are inserted directly into headers, an attacker can inject newline characters (CRLF, \r\n) to split the header block and inject additional headers or response status lines.

For example, consider a Flask route that takes a username from the request and sets a custom header without sanitization:

from flask import Flask, request

app = Flask(__name__)

@app.route("/profile")
def profile():
    user = request.args.get("user", "")
    # Unsafe: user value reflected into a header
    resp = f"User profile for {user}"
    headers = {"X-User": user}
    return resp, 200, headers

If an attacker sends user=alice\r\nX-Content-Type-Options: nosniff, the resulting response can include an injected header that overrides security directives. Basic Auth itself does not cause injection, but it can amplify impact: when authentication is enforced via Authorization headers, an attacker may try to inject additional Authorization lines or other headers to bypass controls, forge origin hints, or manipulate caching behavior. Injection can also occur via the Referer or Origin headers if these are used to construct other headers downstream. In some cases, attackers combine header injection with CRLF injection to perform HTTP response splitting, which may lead to cache poisoning or client-side deception. Because Basic Auth credentials are often validated early, developers may trust the authenticated identity and fail to sanitize reflected values, increasing the risk of authenticated-header injection.

Another relevant pattern is passing Authorization header content to logging, metrics, or custom headers without sanitization. For instance, extracting the token from Authorization and reflecting it in X-Auth-Token can enable header injection if the token contains newline characters. Since Basic Auth tokens are base64-encoded strings, they are not inherently safe; attackers who can influence the client side may supply crafted credentials to probe injection paths.

These issues map to common weaknesses and framework-specific patterns such as CWE-113 (Improper Neutralization of CRLF Sequences) and align with injection aspects of the OWASP API Top 10. Because Flask does not automatically sanitize header values, developers must explicitly validate and encode any user-controlled data before it reaches the response header construction logic.

Basic Auth-Specific Remediation in Flask — concrete code fixes

To mitigate header injection with Basic Auth in Flask, sanitize and strictly validate any user-controlled data before it is placed into headers. Avoid concatenating raw input into header values; instead, use allowlists and strict encoding. Below are concrete, safe patterns.

1. Reject or encode newline characters

Ensure that values used in headers cannot introduce CRLF sequences. Either reject inputs containing \r or \n, or remove/replace them.

import re
from flask import Flask, request

app = Flask(__name__)

SAFE_USER_RE = re.compile(r"^[A-Za-z0-9_.-]{1,64}$")

@app.route("/profile")
def profile():
    user = request.args.get("user", "")
    if not SAFE_USER_RE.match(user):
        return "Invalid user", 400
    resp = f"User profile for {user}"
    headers = {"X-User": user}
    return resp, 200, headers

2. Use an allowlist for known safe values

When feasible, map incoming identifiers to predefined safe values rather than reflecting them directly.

from flask import Flask, request

app = Flask(__name__)

KNOWN_USERS = {"alice", "bob", "charlie"}

@app.route("/profile")
def profile():
    user = request.args.get("user", "")
    if user not in KNOWN_USERS:
        return "Unknown user", 403
    resp = f"User profile for {user}"
    headers = {"X-User": user}
    return resp, 200, headers

3. Avoid reflecting Authorization-derived values into custom headers

If you derive values from the Authorization header, do not reflect raw credentials. Instead, use server-side session mapping or opaque identifiers.

from flask import Flask, request, g
import base64

app = Flask(__name__)

# Example: decode Basic Auth and validate against a server-side mapping
VALID_USERS = {"alice": "password1", "bob": "password2"}

@app.before_request
def authenticate():
    auth = request.headers.get("Authorization", "")
    if not auth.lower().startswith("basic "):
        return "Unauthorized", 401
    try:
        encoded = auth.split(" ", 1)[1]
        decoded = base64.b64decode(encoded).decode("utf-8")
        username, password = decoded.split(":", 1)
        if VALID_USERS.get(username) != password:
            return "Unauthorized", 401
        g.user = username
    except Exception:
        return "Bad credentials", 400

@app.route("/profile")
def profile():
    # Safe: use server-validated identity, not raw input
    headers = {"X-User": g.user}
    return f"User profile for {g.user}", 200, headers

4. Use framework-provided response utilities

When building responses, prefer constructs that do not allow header injection. For custom headers, ensure values are stripped of control characters.

from flask import Flask, request, make_response

app = Flask(__name__)

@app.route("/data")
def data():
    user = request.args.get("user", "")
    # Strip newlines and carriage returns if you must reflect
    safe_user = user.replace("\r", "").replace("\n", "")
    resp = make_response(f"Data for {safe_user}")
    resp.headers["X-Safe-User"] = safe_user
    return resp

5. Apply global safety via a response hook

Add a hook that scrubs headers for dangerous characters across the app.

from flask import Flask, request

app = Flask(__name__)

@app.after_request
def sanitize_headers(response):
    # Remove newlines from all header values
    for key, value in response.headers.items():
        if isinstance(value, str):
            response.headers[key] = value.replace("\r", "").replace("\n", "")
    return response

Mapping to standards

These practices align with secure coding guidance for CRLF injection (CWE-113) and authentication handling (ASVS). They complement the OWASP API Security Top 10 by reducing injection surfaces in both authenticated and unauthenticated paths. Unlike generic scanners, targeted remediation that addresses the intersection of authentication and header handling is essential to prevent abuse of trusted identities.

Frequently Asked Questions

Can header injection still occur if I use HTTPS and Basic Auth?
Yes. HTTPS protects data in transit but does not prevent header injection. If your Flask app reflects user-controlled data into headers over HTTPS, injection remains possible. Always validate and encode header values regardless of transport security.
Does middleBrick detect header injection in Flask Basic Auth setups?
middleBrick scans unauthenticated attack surfaces and tests 12 security checks in parallel, including Input Validation and Header-related behaviors. It reports findings with severity and remediation guidance, but it does not fix or block issues; developers must apply the remediation patterns described.