Crlf Injection in Django with Bearer Tokens
Crlf Injection in Django with Bearer Tokens — how this specific combination creates or exposes the vulnerability
Crlf Injection occurs when an attacker can inject a CRLF sequence (\r\n) into a header or header-like value. In Django, this commonly arises when building HTTP responses or redirect URLs using unsanitized input. When Bearer Tokens are involved—typically passed via the Authorization header or reflected in custom headers—improper handling can enable header injection, response splitting, or token leakage.
Consider a Django view that takes a token from a request and places it into a custom response header without validation:
from django.http import HttpResponse
def profile(request):
token = request.GET.get('token', '')
response = HttpResponse('Profile')
response['X-Auth-Token'] = token
return response
If an attacker supplies Authorization: Bearer abc123\r\nSet-Cookie: session=hijacked as the token, the injected CRLF can create an additional header. Because the Authorization value is reflected into a header, the injected Set-Cookie may execute in the victim’s browser, leading to session fixation or theft. This is especially dangerous when tokens are used for authorization downstream and are assumed to be immutable.
Another scenario involves redirects. If a Django view constructs a redirect URL using an attacker-controlled token or parameter and does not sanitize CRLF, an attacker can inject headers or even a second request line:
from django.http import HttpResponseRedirect
def redirect_to_token(request):
next_token = request.GET.get('token', '')
url = f'https://api.example.com/validate?token={next_token}'
return HttpResponseRedirect(url)
An input like https://evil.com/\r\nSet-Cookie: tracking=malicious in the token could break the redirect into two responses, causing the client to follow the attacker’s crafted header. Because Bearer Tokens often appear in logs, headers, and URLs, they become a vector for token exfiltration if CRLF is not mitigated.
Django’s built-in Host header validation and common middleware do not automatically protect against CRLF when values are placed into headers manually. The risk is elevated when tokens are reflected, logged, or used to build Location or custom headers. Attack patterns include response splitting, HTTP header smuggling, and client-side injection, which can bypass authentication boundaries or poison caches.
Bearer Tokens-Specific Remediation in Django — concrete code fixes
Remediation focuses on strict input validation, avoiding reflection into headers, and using framework-provided utilities. Never directly embed user-controlled values into headers. If you must include a token in a header, treat it as an opaque string and do not concatenate it into header lines manually.
1) Validate and sanitize token input before use. Reject tokens containing CRLF characters:
import re
def sanitize_token(token: str) -> str:
# Remove or reject dangerous characters
if re.search(r'[\r\n]', token):
raise ValueError('Invalid token')
return token.strip()
def profile(request):
token = request.GET.get('token', '')
try:
token = sanitize_token(token)
except ValueError:
return HttpResponseBadRequest('Invalid token')
response = HttpResponse('Profile')
response['X-Auth-Token'] = token
return response
2) Use Django’s redirect utilities instead of building Location headers manually. If you need to redirect with a token, pass it as a query parameter and ensure it is URL-encoded, not concatenated into a raw URL string:
from django.http import HttpResponseRedirect
def redirect_to_token(request):
next_token = request.GET.get('token', '')
if re.search(r'[\r\n]', next_token):
return HttpResponseBadRequest('Invalid token')
# Use Django's redirect with safe URL construction
from urllib.parse import urlencode
params = urlencode({'token': next_token})
url = f'https://api.example.com/validate?{params}'
return HttpResponseRedirect(url)
3) Avoid putting tokens into custom headers when possible. If necessary, treat the token as a single opaque value and assign it directly without additional formatting:
response['X-Auth-Token'] = sanitize_token(request.GET.get('token', ''))
4) For Bearer tokens used in Authorization headers, rely on Django’s built-in authentication schemes rather than manually echoing the header. Use token authentication libraries that handle parsing safely, and ensure token validation does not reflect untrusted input into response headers.
These fixes reduce the attack surface by preventing CRLF injection at the boundary, ensuring tokens are validated before use, and avoiding unsafe concatenation when constructing headers or URLs.