HIGH crlf injectionflaskbasic auth

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.

Frequently Asked Questions

Why is Basic Auth particularly risky for header injection in Flask?
Basic Auth places credentials in the Authorization header, which is often decoded and reused in application code. If the username or password is reflected into other response headers without sanitization, CRLF sequences can split headers and enable injection attacks.
Can middleware or filters fully prevent CRLF Injection in Flask with Basic Auth?
Middleware can help by normalizing or rejecting suspicious characters, but the safest mitigation is to treat all decoded credentials as untrusted, validate them against strict allow-lists, and avoid echoing them into headers or responses.