HIGH crlf injectiondjangopython

Crlf Injection in Django (Python)

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

Crlf Injection occurs when an attacker can inject carriage return (CR, \r) and line feed (\n) characters into an HTTP header, causing the header to be split and additional headers or response content to be injected. In Django, this typically arises when user-controlled data is used to construct HTTP responses, such as the Location header in a redirect or values in Set-Cookie, without proper validation or sanitization. Because Django is a Python web framework, the vulnerability is realized through Python string handling when composing headers, and through Django’s utilities that forward user input into HTTPMessage or HttpResponse structures.

Consider a user-supplied "next" parameter used in a redirect:

next_url = request.GET.get('next', '/')

If this value is passed directly into an HttpResponseRedirect or used to build a Location header, an attacker can supply a payload like https://example.com\r\nContent-Type: text/html\r\n\r\n. The \r\n sequences split the header, injecting a second header that may change content type or trigger response splitting. In Python’s http.client and Django’s HttpResponse, headers are assembled and sent to the WSGI server; injected CR LF can cause the server to treat injected text as a new header or as body content, leading to response splitting, cache poisoning, or XSS in some contexts.

Another common pattern is Set-Cookie construction using user input, for example:

username = request.GET.get('username', 'guest')
response.set_cookie('welcome', f'Hello {username}')

If the cookie value contains \r\n, an attacker can inject additional Set-Cookie headers or malformed attributes. Python’s email and HTTP message libraries used by Django’s HttpResponse can treat \r\n as header delimiters, enabling injection when values are not strictly validated. Even when using Django’s JsonResponse, if the JSON string is later embedded in a header context without escaping, CRLF injection remains possible.

Django does not automatically sanitize header inputs; therefore, any user-controlled string that reaches a header context must be validated. Attack patterns include using CR LF to inject new headers (e.g., Content-Security-Policy), splitting responses to hide malicious content, or bypassing same-site cookie protections by injecting Path or Domain attributes. The framework relies on developers to ensure that such inputs are restricted to safe characters and that no newlines are present.

Python-Specific Remediation in Django — concrete code fixes

Remediation centers on strict input validation and avoiding direct concatenation of user input into HTTP headers. For redirects, use Django’s redirect utilities that accept a view name or a validated absolute URL, and reject any user-provided values containing CR or LF characters.

import re
from django.http import HttpResponseRedirect
from django.urls import reverse

SAFE_URL_CHARS = re.compile(r'^[a-zA-Z0-9_\-\.~%:/?#[\]@!$&\'()*+,;=]*$')

def redirect_to_dashboard(request):
    next_url = request.GET.get('next')
    if next_url is not None:
        # Reject any header-injection-sensitive characters
        if '\n' in next_url or '\r' in next_url:
            return HttpResponseRedirect(reverse('dashboard'))
        # Optionally enforce a safe pattern or absolute same-origin URL
        if not next_url.startswith('/'):
            return HttpResponseRedirect(reverse('dashboard'))
        return HttpResponseRedirect(next_url)
    return HttpResponseRedirect(reverse('dashboard'))

For cookies, avoid embedding raw user input into cookie values that could be concatenated into a Set-Cookie header. Instead, use Django’s cookie API safely and validate values:

from django.http import HttpResponse

username = request.GET.get('username', 'guest')
# Reject newline characters
if '\n' in username or '\r' in username:
    username = 'guest'
response = HttpResponse('OK')
response.set_cookie('welcome', f'Hello {username}')
response.set_cookie('session_id', 'abc123', samesite='Lax', httponly=True)
response['Content-Security-Policy'] = "default-src 'self'"
response['X-Content-Type-Options'] = 'nosniff'
return response

When building custom headers, always sanitize inputs by removing or rejecting control characters:

import re

def sanitize_header_value(value: str) -> str:
    # Remove CR and LF and any non-printable characters
    return re.sub(r'[\r\n\x00-\x1f\x7f]', '', value)

user_comment = request.GET.get('comment', '')
safe_comment = sanitize_header_value(user_comment)
response['X-Comment'] = safe_comment
return response

For JSON-based endpoints, ensure that responses are generated by Django’s serializers rather than manually concatenated strings. This prevents accidental header-like content in JSON that could be misused elsewhere:

from django.http import JsonResponse

def user_profile(request):
    user_data = {'username': request.GET.get('username', 'guest')}
    # Let JsonResponse handle serialization; avoid manual string assembly
    return JsonResponse(user_data, json_dumps_params={'ensure_ascii': False})

Finally, integrate these checks into development practices by adding unit tests that assert the absence of CR/LF in constructed headers and by using middleBrick to scan your endpoints. middleBrick’s scans can surface exposed injection points and provide prioritized findings with remediation guidance, helping you catch misconfigurations before deployment.

Frequently Asked Questions

Can CRLF injection be exploited through Django’s JSON API responses?
Yes, if JSON responses are later used in header contexts (for example, extracting a value into a Location or Set-Cookie header) without escaping, CRLF injection can occur. Always validate and sanitize any user-controlled data before it reaches HTTP headers, regardless of the initial response format.
Does using Django’s HttpResponseRedirect with a validated URL fully prevent CRLF injection?
Using Django’s redirect helpers reduces risk, but you must still validate that the URL does not contain CR or LF characters and that it is either a relative path or an absolute URL you control. Never forward arbitrary user input directly into a redirect.