Crlf Injection in Django
How Crlf Injection Manifests in Django
CRLF injection in Django applications typically occurs when user input is incorporated into HTTP headers or other response components without proper sanitization. The vulnerability arises because carriage return (CR, \r) and line feed (LF, \n) characters can be used to split headers or inject malicious content into HTTP responses.
In Django, several code patterns create CRLF injection opportunities. The most common is when developers use HttpResponse objects and directly insert user-controlled data into headers. For example:
def vulnerable_view(request):
header_value = request.GET.get('header')
response = HttpResponse('Hello')
response['X-Custom-Header'] = header_value
return responseIf an attacker passes ?header=good%0d%0aX-Malicious: bad as a parameter, the response will contain an injected header. Django's header handling doesn't automatically sanitize these characters, making this a real risk.
Another Django-specific manifestation occurs in middleware and custom response handling. Developers often create utility functions to set multiple headers:
def set_security_headers(response, user_agent):
response['X-UA-Compatible'] = 'IE=Edge,chrome=1'
response['X-Content-Type-Options'] = 'nosniff'
response['X-Frame-Options'] = 'DENY'
response['X-User-Agent'] = user_agent # Vulnerable if user_agent is untrusted
return responseCSV generation in Django views also creates CRLF injection opportunities. When creating downloadable CSV files, developers might construct content without proper escaping:
def csv_download(request):
rows = [
['Name', 'Email', 'Notes'],
['Alice', '[email protected]', 'Loves Django\nExtra line'],
['Bob', request.GET.get('email'), request.GET.get('notes')]
]
csv_content = ''
for row in rows:
csv_content += ','.join(row) + '\n'
response = HttpResponse(csv_content, content_type='text/csv')
response['Content-Disposition'] = 'attachment; filename="data.csv"'
return responseThe second row demonstrates how unvalidated input in CSV cells can break the file structure, potentially causing issues in spreadsheet applications or data processing pipelines.
Django-Specific Detection
Detecting CRLF injection in Django applications requires both static analysis and dynamic testing. Static analysis involves reviewing code for patterns where user input flows into HTTP headers or response content without validation.
Code review should focus on these Django-specific patterns:
def review_for_crlf_vulnerabilities(view_functions):
vulnerable_patterns = []
for func in view_functions:
source = inspect.getsource(func)
# Look for header assignments with user input
if re.search(r'response\[.*\].*=.*request\.(GET|POST|COOKIES|META)', source):
vulnerable_patterns.append(func.__name__)
# Look for CSV/CSV-like generation with user input
if re.search(r'\.join.*request\.', source) and re.search(r'\n', source):
vulnerable_patterns.append(func.__name__)
return vulnerable_patternsDynamic testing with automated scanners like middleBrick is more effective for production detection. middleBrick's black-box scanning approach tests the actual running application without requiring source code access. The scanner sends payloads containing CRLF sequences to various endpoints and analyzes responses for injection indicators.
For Django applications, middleBrick specifically tests:
- Header injection through query parameters and POST data
- CSV and text-based response manipulation
- Middleware header processing
- Template rendering with user input
- Static file serving with user-controlled parameters
The scanner's 12 security checks include input validation testing that specifically looks for CRLF injection patterns. When middleBrick detects a vulnerability, it provides the exact request that triggered the issue and the response showing the injection, making remediation straightforward.
middleBrick's scanning process for Django applications takes 5–15 seconds and requires no configuration. Simply provide the base URL of your Django application, and the scanner will automatically discover endpoints and test them for CRLF injection among other vulnerabilities.
Django-Specific Remediation
Remediating CRLF injection in Django applications involves input validation, output encoding, and secure coding practices. The most effective approach is to sanitize input at the boundaries where user data enters your system.
For header values, Django provides the strip_tags utility, but you need additional sanitization for CRLF characters:
from django.utils.html import strip_tags
import re
def sanitize_header_value(value):
"""Remove HTML tags and CRLF characters from header values"""
if not value:
return value
# Remove HTML tags
value = strip_tags(value)
# Remove CR and LF characters
value = re.sub(r'\r|\n', '', value)
return value
def secure_view(request):
header_value = request.GET.get('header', '')
safe_header = sanitize_header_value(header_value)
response = HttpResponse('Hello')
response['X-Custom-Header'] = safe_header
return responseFor CSV generation, Django's csv module provides proper escaping:
import csv
from io import StringIO
def secure_csv_download(request):
buffer = StringIO()
writer = csv.writer(buffer, lineterminator='\n')
rows = [
['Name', 'Email', 'Notes'],
['Alice', '[email protected]', 'Loves Django'],
['Bob', request.GET.get('email', ''), request.GET.get('notes', '')]
]
for row in rows:
# Sanitize each cell
sanitized_row = [sanitize_header_value(cell) for cell in row]
writer.writerow(sanitized_row)
csv_content = buffer.getvalue()
response = HttpResponse(csv_content, content_type='text/csv')
response['Content-Disposition'] = 'attachment; filename="data.csv"'
return responseMiddleware can provide centralized protection for all responses:
class CrlfProtectionMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
response = self.get_response(request)
# Sanitize all header values
for header, value in response.items():
if isinstance(value, str):
response[header] = sanitize_header_value(value)
return responseAdd this middleware to your settings.py:
MIDDLEWARE = [
# ... other middleware ...
'myapp.middleware.CrlfProtectionMiddleware',
]For comprehensive protection, integrate middleBrick's CLI into your development workflow. The middlebrick scan command can be run locally or in CI/CD pipelines to catch CRLF injection vulnerabilities before deployment:
npx middlebrick scan https://your-django-app.com --output=json --threshold=80This continuous scanning approach ensures that as your Django application evolves, new CRLF injection vulnerabilities are caught early. The GitHub Action integration can fail pull requests if security scores drop, preventing vulnerable code from reaching production.