HIGH header injectiondjangobasic auth

Header Injection in Django with Basic Auth

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

Header Injection occurs when user-controlled data is placed into HTTP response headers without proper validation or encoding. In Django, combining Basic Authentication with insecure header handling can expose sensitive information or enable response splitting attacks. Basic Auth in Django is commonly implemented using the django.contrib.auth module and middleware that parses the Authorization header. When a developer uses raw header values from the request—such as custom headers or the Authorization header itself—and directly copies them into other response headers, an attacker can inject newline characters (\r\n) to manipulate the response structure.

For example, consider a Django view that reads a custom header and reflects it in the response:

import base64
from django.http import HttpResponse
from django.views.decorators.http import require_http_methods

@require_http_methods(["GET"])
def profile(request):
    auth_header = request.META.get("HTTP_AUTHORIZATION", "")
    if auth_header.startswith("Basic "):
        encoded = auth_header.split(" ")[1]
        decoded = base64.b64decode(encoded).decode("utf-8")
        username, password = decoded.split(":", 1)
        # Vulnerable: directly using user input in a header
        response = HttpResponse(f"Authenticated as {username}")
        response["X-User-Name"] = username  # Injection point
        return response
    return HttpResponse(status=401)

If the username contains a sequence like \r\nX-Injected: malicious, and the framework does not sanitize it, the injected header will be appended to the response. This can lead to HTTP response splitting, cache poisoning, or the disclosure of sensitive headers to downstream proxies. In the context of Basic Auth, the decoded credentials may also be inadvertently exposed through reflected headers or logs if header values are not sanitized. Because Basic Auth transmits credentials in an easily decoded base64 string, any leakage through headers increases the risk of credential exposure.

Another angle involves the WWW-Authenticate header. If Django is misconfigured to include user-controlled data within this header—such as a realm value derived from request parameters—an attacker can inject additional directives. For instance, an invalid realm containing a newline could break the header format and cause clients to misinterpret authentication challenges, potentially bypassing intended protections or triggering client-side errors that reveal stack traces or internal behavior.

Because this scan tests unauthenticated attack surfaces, middleBrick flags such header manipulation patterns under the Data Exposure and Input Validation checks. The scanner does not modify headers but identifies where user input reaches response headers without canonicalization or strict allowlisting.

Basic Auth-Specific Remediation in Django — concrete code fixes

To prevent Header Injection when using Basic Auth in Django, ensure that any data derived from user input is strictly validated and never directly reflected in headers. Use allowlisted values for realms and avoid including raw usernames or passwords in headers. The following patterns demonstrate secure implementations.

Secure Header Handling with Basic Auth

Instead of reflecting user data in headers, use server-side identifiers. For example, after validating credentials, assign a fixed session-based identifier or use Django’s session framework:

import base64
from django.http import HttpResponse
from django.views.decorators.http import require_http_methods
from django.contrib.auth import authenticate

@require_http_methods(["GET"])
def secure_profile(request):
    auth_header = request.META.get("HTTP_AUTHORIZATION", "")
    if auth_header.startswith("Basic "):
        encoded = auth_header.split(" ")[1]
        decoded = base64.b64decode(encoded).decode("utf-8")
        username, password = decoded.split(":", 1)
        user = authenticate(request, username=username, password=password)
        if user is not None:
            # Use a safe, non-user-controlled value
            response = HttpResponse(f"Authenticated as {user.username}")
            response["X-Request-ID"] = "fixed-identifier"  # Safe static value
            return response
    return HttpResponse(status=401)

If you must include a username in a header, enforce strict allowlisting and encoding. For example, use a sanitized slug and avoid newlines:

import re
import base64
from django.http import HttpResponse
from django.views.decorators.http import require_http_methods

SAFE_USERNAME_RE = re.compile(r"^[a-zA-Z0-9_.-]{1,32}$")

@require_http_methods(["GET"])
def sanitized_header_profile(request):
    auth_header = request.META.get("HTTP_AUTHORIZATION", "")
    if auth_header.startswith("Basic "):
        encoded = auth_header.split(" ")[1]
        decoded = base64.b64decode(encoded).decode("utf-8")
        username, password = decoded.split(":", 1)
        if SAFE_USERNAME_RE.match(username):
            response = HttpResponse(f"Authenticated as {username}")
            # Only include if absolutely necessary and sanitized
            response["X-Safe-User"] = username.replace(" ", "-").lower()
            return response
    return HttpResponse(status=401)

For the WWW-Authenticate header, always use static strings for realms and avoid concatenating user input:

from django.http import HttpResponse

def custom_401(request, exception):
    response = HttpResponse(status=401)
    response["WWW-Authenticate"] = 'Basic realm="secure-api", charset="UTF-8"'  # Static realm
    return response

These practices align with Django’s security model and reduce the risk of injection. middleBrick’s scans detect whether headers reflect unsanitized input and whether WWW-Authenticate includes dynamic content, providing findings mapped to OWASP API Top 10 and Input Validation checks.

For teams using the middleBrick CLI, running middlebrick scan <url> can validate these headers in unauthenticated scans. The Pro plan supports continuous monitoring to catch regressions, and the GitHub Action can enforce header safety in CI/CD pipelines.

Frequently Asked Questions

Can header injection in Django with Basic Auth lead to credential exposure?
Yes. If usernames or decoded credentials are reflected in response headers without sanitization, they may be exposed through injected headers or logs. Always avoid placing raw authentication data in headers.
How does middleBrick detect header injection risks in Django Basic Auth endpoints?
middleBrick scans unauthenticated endpoints and checks whether user-controlled input from headers or query parameters appears in response headers without validation. Findings are reported under Data Exposure and Input Validation categories with remediation guidance.