HIGH crlf injectiondjangomutual tls

Crlf Injection in Django with Mutual Tls

Crlf Injection in Django with Mutual Tls — how this specific combination creates or exposes the vulnerability

Crlf Injection occurs when user-controlled data is inserted into HTTP headers without proper sanitization, allowing an attacker to inject newline characters (%0D %0A) to split headers and inject malicious content. In Django, this typically manifests via the Host header, X-Forwarded-For, or custom headers that are later reflected in responses or logs. With Mutual Tls (mTLS), the server requests and validates a client certificate, adding a layer of authentication. While mTLS does not directly sanitize headers, it can change the request context: for example, front-end terminators (load balancers or API gateways) may terminate TLS and forward requests to Django with modified or trusted headers like X-Client-Cert or SSL_CLIENT_VERIFY. If Django then uses these headers in Location, Set-Cookie, or WWW-Authenticate values without validation, the presence of mTLS may create a false sense of security while the underlying header injection remains exploitable.

Consider a Django view that redirects based on a next parameter or uses X-Forwarded-For to build a custom header. An attacker can supply a crafted header such as X-Forwarded-Host: example.com%0D%0aSet-Cookie:%20session=attacker. If Django passes this to HttpResponse headers or constructs a redirect without sanitization, the injected CRLF can set arbitrary cookies or split the response, leading to cache poisoning, HTTP response splitting, or cross-user cookie manipulation. With mTLS, the server might log the client certificate subject and include it in logs or headers; if those logs or headers are later reflected without sanitization, the CRLF payload can be stored and reused in log injection or header smuggling scenarios.

The combination of mTLS and Django’s header handling can also affect observability and filtering. MiddleBrick scans this attack surface by checking Authentication, Input Validation, and Data Exposure across unauthenticated endpoints. Even when mTLS is enforced at the edge, the application must treat all incoming headers as untrusted. For example, a view that reads request.META.get('HTTP_X_FORWARDED_HOST') and uses it in a redirect is vulnerable if the value contains CRLF characters. MiddleBrick’s checks for BFLA/Privilege Escalation and Property Authorization help surface these risky patterns by correlating authentication context with header usage.

Real-world mappings include OWASP API Top 10 2023:5 — Injection, and relevant CWE entries such as CWE-93 (CRLF Injection). In practice, the exploit chain involves an attacker supplying a malicious header, the Django app reflecting it into a redirect or Set-Cookie, and the client’s browser or downstream service interpreting the injected control characters. This can lead to session fixation, cache poisoning, or unauthorized redirects. Because mTLS verifies client identity, developers may overlook header validation, assuming the channel is trusted. However, validation must occur at the application layer regardless of transport security.

To detect this during a scan, tools can submit header values containing sequences like %0D%0ASet-Cookie: and inspect whether the response preserves the injection. MiddleBrick runs checks such as Input Validation and Data Exposure in parallel, testing how headers are handled and whether sensitive data or executable sequences leak. Even with mTLS, the scan verifies that responses do not reflect unsanitized input, ensuring that header manipulation does not compromise integrity.

Mutual Tls-Specific Remediation in Django — concrete code fixes

Remediation focuses on strict header validation and safe construction of responses in Django, regardless of mTLS presence. Always treat values from request.META, headers, and forwarded fields as untrusted. Use Django’s built-in utilities to sanitize and validate before including them in redirects, cookies, or custom headers.

Example 1: Safe redirect handling. Do not use user input directly in HttpResponseRedirect. Instead, validate against an allowlist of hostnames and use urllib.parse.urljoin to build absolute URLs safely.

from urllib.parse import urljoin, urlparse
from django.http import HttpResponseRedirect
from django.conf import settings

def safe_redirect(request):
    next_url = request.GET.get('next', '/')
    parsed = urlparse(next_url)
    # Allowlist approach: restrict to known hosts or same host
    allowed_hosts = getattr(settings, 'ALLOWED_REDIRECT_HOSTS', ['example.com'])
    if parsed.hostname not in allowed_hosts:
        # Fallback to default
        next_url = '/'
    # Ensure no CRLF in the final URL; urljoin normalizes but validate further if needed
    safe_url = urljoin(request.build_absolute_uri(), next_url)
    return HttpResponseRedirect(safe_url)

Example 2: Secure cookie setting with explicit parameters. Avoid reflecting headers into cookie values. If you must include client identity, encode and sign it rather than concatenating raw strings.

from django.http import HttpResponse
import json

def set_safe_cookie_view(request):
    resp = HttpResponse('OK')
    # Do not use raw headers; use verified certificate fields if needed
    # For example, extract a subjectAltName claim via mTLS verification at the edge
    client_id = 'verified-id-123'  # obtained from mTLS verification, not from request
    payload = json.dumps({'client_id': client_id})
    resp.set_cookie('session', payload, httponly=True, samesite='Lax', secure=True)
    return resp

Example 3: Custom header handling with strict newline filtering. If you propagate a header like X-Client-Cert, ensure it does not contain control characters. Use a sanitization function that strips or replaces CRLF sequences.

import re
from django.http import HttpResponse

SANITIZE_CRLF = re.compile(r'[\r\n]')

def sanitize_header_value(value: str) -> str:
    return SANITIZE_CRLF.sub('', value)

def view_with_custom_header(request):
    client_cert_subject = request.META.get('SSL_CLIENT_SUBJECT', '')
    safe_subject = sanitize_header_value(client_cert_subject)
    resp = HttpResponse('Processed')
    resp['X-Client-Subject'] = safe_subject
    return resp

Example 4: Middleware to reject headers containing CRLF. This provides a global safeguard for all incoming requests, complementing mTLS enforcement at the edge.

from django.utils.deprecation import MiddlewareMixin
import re

CRLF_RE = re.compile(r'[\r\n]')

class CrlfHeaderValidationMiddleware(MiddlewareMixin):
    def process_request(self, request):
        for key, value in request.headers.items():
            if CRLF_RE.search(value):
                raise SuspiciousOperation(f'CRLF detected in header {key}')
        return None

These examples emphasize defense in depth: mTLS handles peer authentication at the transport layer, but the application must still validate and sanitize header-derived data. Combine these practices with runtime scans from MiddleBrick to continuously verify that headers are handled safely and that no injection vectors remain.

Frequently Asked Questions

Does mTLS prevent Crlf Injection in Django?
No. Mutual Tls authenticates the client at the transport layer but does not sanitize HTTP headers. CRLF Injection is an application-level issue and must be addressed via header validation and safe response construction in Django.
How can I test my Django app for CRLF Injection when mTLS is in use?
Send requests with crafted headers containing %0D%0A sequences (e.g., X-Forwarded-Host: example.com%0D%0aSet-Cookie:%20foo=bar) and inspect responses for reflected injection. Use a scanner like MiddleBrick to automate checks across authentication and header handling while accounting for mTLS context.