Http Request Smuggling in Django with Bearer Tokens
Http Request Smuggling in Django with Bearer Tokens — how this specific combination creates or exposes the vulnerability
HTTP request smuggling arises when an HTTP request is parsed differently by a frontend proxy/load balancer and the Django application, allowing attackers to smuggle requests across trust boundaries. In Django, this typically involves how the request body and headers are consumed when CONTENT_LENGTH and TRANSFER_ENCODING are both present, or when requests are split or encoded in ways the WSGI server and Django interpret inconsistently.
Bearer tokens complicate this because they are often passed in the Authorization header (e.g., Authorization: Bearer <token>) and are sometimes also placed in query parameters or cookies. When a proxy normalizes or removes headers before forwarding to Django, the effective request seen by Django may lack the Authorization header, while the proxy retains it. This mismatch can allow a smuggled request to bypass intended authentication boundaries: an attacker can craft a request that, when processed by the frontend, associates a request with a valid Bearer token, but by the time Django parses it, the token is absent or different, leading to privilege confusion or unauthorized access.
An example scenario: a frontend terminates TLS and forwards requests to Django over HTTP. If the frontend does not strictly enforce header ordering and body framing, an attacker can send a request with both Content-Length: 0 and Transfer-Encoding: chunked. The frontend may process Transfer-Encoding and consume the body, but forwards a request to Django with Content-Length: 0, causing Django to treat leftover body bytes as the next request. If the first request contains a Bearer token and the second does not, Django may process the second request in the context of the first, potentially exposing privileged operations to unauthenticated calls.
Django’s own development server is not production-grade and should not be relied upon for header/body normalization. In production, common stacks (e.g., Nginx or cloud load balancers in front of Gunicorn/uWSGI) must be configured to reject ambiguous requests. Without strict rules, smuggling can expose endpoints that rely on Bearer tokens for authentication, as token validation may be inconsistently applied across the request pipeline.
To detect this class of issue, scans test for inconsistent handling of Content-Length and Transfer-Encoding, header manipulation, and token placement in different message parts. Findings highlight whether a Django deployment can be tricked into processing a request where authentication context diverges between the proxy and the application, which is especially critical for Bearer token–based APIs.
Bearer Tokens-Specific Remediation in Django — concrete code fixes
Remediation focuses on ensuring consistent parsing of headers and body, enforcing strict authorization handling, and validating the presence and format of Bearer tokens. Below are concrete, production-ready examples for Django.
1. Enforce Authorization header presence and format
Create a middleware or decorator that validates the Authorization header on every request. This ensures token presence and correct scheme before the view runs.
import re
from django.http import HttpResponseForbidden
from functools import wraps
def validate_bearer_token(view_func):
"""Decorator to enforce Bearer token presence and format."quot;
def wrapped(request, *args, **kwargs):
auth = request.META.get('HTTP_AUTHORIZATION', '')
if not auth.startswith('Bearer '):
return HttpResponseForbidden('Invalid authorization header.')
token = auth.split(' ', 1)[1].strip()
if not re.match(r'^[A-Za-z0-9\-_=]+\.[A-Za-z0-9\-_=]+\.?[A-Za-z0-9\-_=]*$', token):
return HttpResponseForbidden('Invalid token format.')
request.token = token
return view_func(request, *args, **kwargs)
return wrapped
@validate_bearer_token
def my_protected_view(request):
# request.token is guaranteed to be present and well-formed
return HttpResponse('OK')
2. Normalize headers at the proxy/load balancer
Configure your frontend (e.g., Nginx) to strip duplicate headers and enforce a canonical Authorization header before forwarding to Django. Example Nginx configuration:
location /api/ {
# Ensure only the last Authorization header is forwarded
if ($sent_http_authorization = '') {
set $auth $http_authorization;
}
proxy_set_header Authorization $auth;
# Reject requests with both Content-Length and Transfer-Encoding
if ($http_transfer_encoding != '') {
return 400;
}
proxy_pass http://django_app;
}
3. Use Django middleware to reject ambiguous body framing
Add middleware that rejects requests where both Content-Length and Transfer-Encoding are present, as this is a common smuggling indicator.
from django.http import HttpResponseBadRequest
class BodyFramingMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
content_length = request.META.get('CONTENT_LENGTH')
transfer_encoding = request.META.get('HTTP_TRANSFER_ENCODING')
if content_length is not None and transfer_encoding is not None:
return HttpResponseBadRequest('Ambiguous body framing.')
return self.get_response(request)
4. Always use HTTPS and avoid token leakage in URLs
Ensure Bearer tokens are never passed in query strings or logs. Configure Django to treat the Authorization header as the sole source of credentials and disable session authentication for API endpoints where tokens are used.
# settings.py
SESSION_COOKIE_SECURE = True
CSRF_COOKIE_SECURE = True
SECURE_SSL_REDIRECT = True
By combining strict header validation, proxy-level normalization, and rejection of malformed framing, Django services can safely handle Bearer tokens even when deployed behind complex frontends.