Crlf Injection in Flask with Basic Auth
Crlf Injection in Flask with Basic Auth — how this specific combination creates or exposes the vulnerability
Crlf Injection occurs when user-controlled data is reflected into HTTP headers without sanitization, allowing an attacker to inject CRLF sequences (\r\n) to split headers and inject new ones. When Basic Auth is used in Flask, the Authorization header value is typically decoded from Base64 to username:password and often passed into application logic or echoed into response headers without validation. This combination creates a path for injection if the application embeds the decoded credentials into other headers, logs, or error messages that are later reflected back in HTTP responses.
For example, if a Flask route decodes the Authorization header and includes the username in a custom header or a JSON response without proper sanitization, an attacker-supplied username containing \r\n can inject additional headers such as Set-Cookie or Content-Type. Because Basic Auth credentials are commonly handled in middleware or helper functions, inconsistent sanitization across routes increases the likelihood of accidental exposure. An attacker can also leverage CRLF Injection to perform HTTP response splitting, leading to cache poisoning or client-side manipulation, especially when responses are cached or shared across users.
In a black-box scan, middleBrick tests inputs that originate from the Authorization header and checks for unexpected header injection in responses. The 12 security checks include Input Validation and Data Exposure, which help detect whether injected CRLF sequences result in reflected newlines in headers or body. Because the attack surface involves header manipulation, the scanner validates whether encoded credentials are handled safely before being used in any downstream logic that might influence HTTP response construction.
Basic Auth-Specific Remediation in Flask — concrete code fixes
To prevent Crlf Injection when using Basic Auth in Flask, ensure that any data derived from the Authorization header is sanitized before being used in HTTP headers, logs, or responses. The safest approach is to avoid echoing credentials entirely; if you must use them, treat them as untrusted input and validate or encode them rigorously.
Example: Unsafe handling of Basic Auth
from flask import Flask, request, make_response
import base64
app = Flask(__name__)
@app.route('/profile')
def profile():
auth = request.headers.get('Authorization', '')
if auth.startswith('Basic '):
encoded = auth.split(' ')[1]
decoded = base64.b64decode(encoded).decode('utf-8')
username, password = decoded.split(':', 1)
# Unsafe: username used directly in a custom header
response = make_response({'message': 'Welcome', 'user': username})
response.headers['X-User'] = username # Potential injection point
return response
return {'error': 'Unauthorized'}, 401
Example: Safe handling with sanitization
import re
from flask import Flask, request, make_response
import base64
app = Flask(__name__)
# Allow only alphanumeric and a few safe symbols; reject control characters and line breaks
def safe_username(value):
return bool(re.match(r'^[A-Za-z0-9._-]{1,64}$', value))
@app.route('/profile')
def profile():
auth = request.headers.get('Authorization', '')
if auth.startswith('Basic '):
try:
encoded = auth.split(' ')[1]
decoded = base64.b64decode(encoded).decode('utf-8')
username, password = decoded.split(':', 1)
except Exception:
return {'error': 'Bad credentials'}, 400
if not safe_username(username):
return {'error': 'Invalid username format'}, 400
# Safe: username is validated and not reflected in headers
response = make_response({'message': 'Welcome', 'user': username})
# If you must set a header, ensure no CRLF characters are present
response.headers['X-User'] = username.replace('\r', '').replace('\n', '')
return response
return {'error': 'Unauthorized'}, 401
General Flask remediation practices
- Never directly embed Authorization-derived values into response headers; if necessary, apply strict allow-lists and remove or escape \r and \n characters.
- Use Flask’s built-in abstractions for authentication where possible, and keep credential handling isolated from response construction.
- Log securely by avoiding full Authorization header dumps; log only non-sensitive metadata and use structured logging with sanitization.
- Validate and normalize all inputs, including headers, query parameters, and cookies, using allow-lists rather than block-lists to reduce bypass risks.