Man In The Middle in Django with Bearer Tokens
Man In The Middle in Django with Bearer Tokens — how this specific combination creates or exposes the vulnerability
A Man In The Middle (MitM) attack against a Django service that uses Bearer Tokens occurs when an attacker intercepts or alters traffic between the client and the server. Because Bearer Tokens are typically transmitted in HTTP headers (most commonly Authorization: Bearer <token>), if the channel is not strictly protected, intercepted tokens can be reused to impersonate users or escalate privileges.
In Django, this risk is present when endpoints rely solely on token validation without enforcing transport integrity. For example, an API endpoint that reads the token from the Authorization header but does not require HTTPS allows an attacker on a shared network to capture the token and make authenticated requests. Even if your Django app validates the token signature, the token itself is exposed in transit, enabling session hijacking.
The combination is particularly dangerous when tokens are long-lived or when the API serves both web and native clients without strict transport enforcement. Django’s default settings do not enforce HTTPS unless explicitly configured, so developers must ensure that all token-bearing requests occur over TLS. Without this, the token is effectively an unprotected credential traversing the network.
Additionally, improper host header validation or missing HTTP Strict Transport Security (HSTS) can redirect or downgrade connections, increasing the MitM surface. An attacker might also exploit insecure CORS policies to trick a browser into sending the Authorization header to a malicious origin, though the header itself remains under server control. The core issue is that Bearer Tokens convey authentication, but if the transport layer is compromised, the token’s integrity is nullified regardless of backend validation logic.
Other vectors include logging or error handling that inadvertently expose the Authorization header, or misconfigured reverse proxies and load balancers that terminate TLS inconsistently. In environments where traffic passes through multiple hops, failing to validate end-to-end encryption means any intermediate node could theoretically inspect headers. Therefore, protecting against MitM with Bearer Tokens in Django requires both cryptographic transport enforcement and disciplined handling of sensitive headers.
Bearer Tokens-Specific Remediation in Django — concrete code fixes
To mitigate MitM risks when using Bearer Tokens in Django, enforce HTTPS across your application and ensure tokens are only transmitted and stored securely. Begin by configuring Django to require secure connections and to treat all requests as secure when behind a trusted proxy.
# settings.py
SECURE_SSL_REDIRECT = True
SESSION_COOKIE_SECURE = True
CSRF_COOKIE_SECURE = True
SECURE_HSTS_SECONDS = 31536000
SECURE_HSTS_INCLUDE_SUBDOMAINS = True
SECURE_HSTS_PRELOAD = True
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
Next, validate the Authorization header in a dedicated authentication class. This approach integrates cleanly with Django REST Framework and ensures that only properly formatted Bearer Tokens are accepted.
# authentication.py
from rest_framework import authentication
from rest_framework import exceptions
import re
class BearerTokenAuthentication(authentication.BaseAuthentication):
"""
Custom Bearer Token authentication for Django REST Framework.
Expects the Authorization header in the format: Bearer <token>
"""
def authenticate(self, request):
auth_header = request.headers.get('Authorization')
if not auth_header:
return None
# Ensure header follows Bearer <token> format
match = re.match(r'^Bearer\s+(\S+)$', auth_header)
if not match:
return None
token = match.group(1)
# Replace with your token validation logic, e.g., verify against a model
# user = validate_token(token)
# if not user:
# raise exceptions.AuthenticationFailed('Invalid token')
# For illustration, we return a placeholder user and token
# In production, map the token to an actual user object
return (None, token)
Use this authenticator in your views or globally to ensure consistent validation. Additionally, avoid logging Authorization headers by customizing logging filters to strip sensitive data.
# settings.py
import sys
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'filters': {
'mask_auth_header': {
'()': 'django.utils.log.CallbackFilter',
'callback': lambda record: not record.getMessage().__contains__('Authorization'),
},
},
'handlers': {
'console': {
'class': 'logging.StreamHandler',
'filters': ['mask_auth_header'],
},
},
'loggers': {
'django': {
'handlers': ['console'],
'level': 'INFO',
},
},
}
For production-grade security, combine these settings with infrastructure-level TLS enforcement and regular token rotation. Tools like the middleBrick CLI can help you verify that your endpoints correctly require HTTPS and reject cleartext token transmission.
Example verification using the middleBrick CLI:
middlebrick scan https://api.example.com/health
The scan will indicate whether the endpoint exposes sensitive headers or lacks transport security, helping you confirm that Bearer Tokens are transmitted only over encrypted channels.