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.