HIGH crlf injectiondjangobasic auth

Crlf Injection in Django with Basic Auth

Crlf Injection in Django with Basic Auth — how this specific combination creates or exposes the vulnerability

Crlf Injection occurs when an attacker can inject a carriage return (CR, \r) and line feed (LF, \n) sequence into a header or status line, causing the header to be split and additional headers or response content to be injected. In Django, this risk exists when user-controlled data is reflected in HTTP headers without strict validation or encoding. When Basic Authentication is used, the Authorization header is typically parsed by Django or middleware, and parts of it (such as the username or password) may be logged, echoed into custom headers, or used to construct other header values. If any of those values are reflected into downstream headers without sanitization, an attacker can inject CR+LF sequences to inject new headers like Location, Set-Cookie, or even split the response body.

Consider a scenario where a Django view decodes the Basic Auth credentials and places the username into a custom header for debugging or tracking purposes. A username like admin\r\nX-Injected: true would be decoded by Django’s Basic Auth handling, and if the application then passes that username into a response header without validation, the injected header will be interpreted by the client as a separate header. Because Basic Auth credentials are often base64-encoded in the Authorization header, developers may mistakenly assume the content is opaque, but if any decoding and reflection occurs without strict allow-lists or encoding, injection is possible.

Another relevant pattern is when Django applications integrate with external services that rely on Basic Auth and return headers that are later forwarded or included in Django responses. If an attacker controls the upstream service’s response and it contains injected CR+LF sequences, and Django reflects those values into its own headers, the injection can propagate. This is especially dangerous when status codes or reason phrases are constructed using user-influenced data, as CRLF injection can allow response splitting, cache poisoning, or client-side header manipulation.

Because the scan category Input Validation tests for header injection points, Crlf Injection will be surfaced alongside findings for missing header validation and unsafe reflection patterns. Remediation focuses on disallowing CR and LF characters in any header-affecting input and ensuring that any data placed into headers is properly encoded or omitted.

Basic Auth-Specific Remediation in Django — concrete code fixes

To prevent Crlf Injection in Django when using Basic Authentication, ensure that any user-controlled values reflected into HTTP headers are strictly validated and encoded. The following patterns demonstrate secure handling of Basic Auth credentials and safe header construction.

Secure Basic Auth parsing and header usage

Instead of manually parsing the Authorization header, rely on Django’s built-in authentication mechanisms or a well-vetted library. When you must extract credentials, validate and sanitize any values before using them in headers.

import base64
import re
from django.http import HttpResponse
from django.views import View

# Safe Basic Auth extraction with strict validation
SAFE_USERNAME_RE = re.compile(r'^[A-Za-z0-9._\-]+$')  # allow-list approach

def safe_get_basic_auth_credentials(request):
    auth = request.META.get('HTTP_AUTHORIZATION', '')
    if not auth.lower().startswith('basic '):
        return None, None
    try:
        encoded = auth.split(' ', 1)[1].strip()
        decoded = base64.b64decode(encoded).decode('utf-8')
    except Exception:
        return None, None
    # Expecting format: username:password
    if ':' not in decoded:
        return None, None
    username, password = decoded.split(':', 1)
    if not SAFE_USERNAME_RE.match(username):
        return None, None
    return username, password

class ProfileView(View):
    def get(self, request):
        username, _ = safe_get_basic_auth_credentials(request)
        response = HttpResponse('OK')
        if username:
            # Safe: username is validated and does not contain CR/LF
            response['X-User'] = username
        return response

Avoid directly using request.user or decoded credentials in headers that may be controlled by external systems. Always treat the Authorization header as opaque when possible, and do not reflect raw credential segments into response headers.

Disallow CR and LF in header-affecting input

Add validation that explicitly rejects carriage return and line feed characters in any data destined for headers. This can be enforced at the model, form, or middleware level depending on your architecture.

FORBIDDEN_CHARS = {'\r', '\n'}

def validate_no_crlf(value):
    if any(c in value for c in FORBIDDEN_CHARS):
        raise ValueError('Header-affecting values must not contain CR or LF')

# Example usage in a serializer or clean method
from django.core.exceptions import ValidationError

def clean_username(self):
    username = self.cleaned_data.get('username')
    try:
        validate_no_crlf(username)
    except ValueError:
        raise ValidationError('Invalid characters in username')
    return username

Middleware approach for global protection

For applications that construct custom headers from request or user data, a lightweight middleware can sanitize outgoing headers to remove or replace CR and LF characters before the response is sent.

class CrlfHeaderMiddleware:
    def __init__(self, get_response):
        self.get_response = get_response

    def __call__(self, request):
        def sanitized_response(response):
            # Remove CR/LF from header values to prevent injection
            for header, value in list(response.headers.items()):
                if '\r' in value or '\n' in value:
                    response.headers[header] = value.replace('\r', '').replace('\n', '')
            return response
        response = self.get_response(request)
        response.add_early_hook('append_headers', sanitized_response)
        return response

Use the middleware judiciously and combine it with input validation. The preferred approach is to avoid reflecting untrusted data in headers entirely; if reflection is necessary, strict allow-lists and character filtering are essential.

Frequently Asked Questions

Can Crlf Injection be exploited through Basic Auth headers even if credentials are base64-encoded?
Yes. While the Authorization header value is base64-encoded, if a Django application decodes it and reflects parts of it (e.g., username) into other headers without validation, an attacker can inject CR+LF sequences in the decoded username or password and manipulate downstream headers.
Does middleBrick’s Input Validation check detect CRLF injection risks in Django headers?
Yes. middleBrick’s Input Validation checks identify places where CR or LF characters could be used to split headers. Findings include specific guidance to disallow CR/LF in header-affecting data and to validate or encode values before reflection.