HIGH header injectiondjangomutual tls

Header Injection in Django with Mutual Tls

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

Header Injection in Django occurs when untrusted input is reflected into HTTP headers without validation, enabling attackers to inject additional headers, split headers, or manipulate the response chain. When Mutual Transport Layer Security (Mutual TLS) is in use, the client presents a certificate during the TLS handshake, and the server validates it before the application layer sees the request. This adds a strong authentication boundary at the transport layer, but it does not automatically protect the application from header-level attacks originating from an authenticated client or from a misconfigured proxy that forwards headers into Django.

In a Mutual TLS setup, Django typically terminates TLS at a reverse proxy or load balancer (such as Nginx or HAProxy) that performs the client certificate validation and forwards requests to the application. If the proxy normalizes or selectively forwards headers (for example, stripping and re-adding headers), it can inadvertently introduce inconsistencies. An attacker who can control request parameters—such as query strings, form fields, or JSON keys—might exploit Django’s header-setting mechanisms (e.g., HttpResponse['X-Custom-Header'] or response['Location'] in redirects) to inject malicious headers like Set-Cookie, Location, or Refresh. These injected headers can lead to session fixation, open redirects, or cache poisoning, even when Mutual TLS is enforced, because the vulnerability resides in how Django processes and reflects data after the TLS layer has authenticated the client.

Mutual TLS also influences logging and monitoring. Since the client identity is available from the certificate, logs may include the certificate subject or serial number, which can aid incident response. However, if developers assume that Mutual TLS alone prevents header manipulation, they may overlook input validation and output encoding in Django views. For example, a view that uses request.META.get('HTTP_X_FORWARDED_PROTO') to construct a redirect without validation can be abused if the proxy does not properly enforce header integrity. The combination of Mutual TLS and header injection is particularly dangerous in APIs where JSON responses are used to set cookies or redirect clients, because attackers can chain certificate-based authentication with header injection to escalate impact across trust boundaries.

Mutual Tls-Specific Remediation in Django — concrete code fixes

Remediation focuses on strict input validation, safe header handling, and explicit proxy configuration. In Django, always treat incoming headers and proxy-derived values as untrusted, even when Mutual TLS is used. Use Django’s built-in utilities for setting headers and avoid string concatenation when constructing values such as Location or Set-Cookie.

Example 1: Safe redirect with validation

from django.http import HttpResponseRedirect
from django.utils.http import is_safe_url

def redirect_view(request):
    next_url = request.GET.get('next', '/')
    # Ensure the URL is safe and uses allowed schemes
    if not is_safe_url(url=next_url, allowed_hosts={request.get_host()}, require_https=request.is_secure()):
        next_url = '/'
    return HttpResponseRedirect(next_url)

This prevents open redirects via the next parameter, which could otherwise be used to inject a malicious Location header.

Example 2: Safe header setting with sanitization

from django.http import HttpResponse

def custom_header_view(request):
    user_value = request.GET.get('label', 'default')
    # Sanitize input: allow only alphanumeric, dash, underscore
    import re
    safe_value = re.sub(r'[^A-Za-z0-9_-]', '', user_value)
    response = HttpResponse('OK')
    response['X-Content-Summary'] = f'report-{safe_value}'
    return response

By sanitizing the input, you prevent header injection via reflected values. Never set headers directly from raw user input.

Example 3: Configure proxy headers correctly in Django settings

# settings.py
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
USE_X_FORWARDED_HOST = False  # Avoid using X-Forwarded-Host unless strictly necessary
USE_X_FORWARDED_PORT = False

Ensure your reverse proxy (e.g., Nginx) is configured to strip or strictly validate incoming proxy headers before forwarding them to Django. Mutual TLS should be enforced at the proxy, and the proxy should only forward trusted headers.

Example 4: Nginx configuration snippet enforcing Mutual TLS and sanitizing forwarded headers

server {
    listen 443 ssl;
    ssl_certificate /etc/ssl/certs/server.crt;
    ssl_certificate_key /etc/ssl/private/server.key;
    ssl_client_certificate /etc/ssl/certs/ca.crt;
    ssl_verify_client on;

    location /api/ {
        proxy_pass http://django_app;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-Proto $scheme;
        # Explicitly drop untrusted headers
        proxy_set_header Authorization "";
        proxy_hide_header Authorization;
    }
}

This ensures that only clients with valid certificates can reach Django, and that potentially dangerous headers are not inadvertently passed through.

Frequently Asked Questions

Does Mutual TLS prevent header injection in Django?
No. Mutual TLS authenticates the client at the transport layer but does not sanitize inputs or validate how Django sets headers. Header injection must be addressed in application code and proxy configuration.
How can I test for header injection in a Django app behind Mutual TLS?
Use the middleBrick CLI to scan the endpoint: middlebrick scan . The scan includes header reflection checks and will flag unsafe header usage even when Mutual TLS is enforced.