Security Misconfiguration in Django with Bearer Tokens
Security Misconfiguration in Django with Bearer Tokens — how this specific combination creates or exposes the vulnerability
Bearer tokens are commonly used for HTTP API authentication. In Django, misconfigurations around how these tokens are validated, stored, and transmitted can expose applications to unauthorized access. A typical misconfiguration occurs when token validation is performed inconsistently across views or when developers inadvertently allow token leakage in logs, error messages, or via insecure transport.
One common pattern is using AuthenticationMiddleware designed for session-based auth while also accepting Bearer tokens via custom headers. If the token validation logic is incomplete or bypassed for certain endpoints, an attacker can access protected resources without proper credentials. For example, failing to enforce HTTPS allows tokens to be captured in transit (MITM), and not setting the HttpOnly and Secure flags on cookies that store tokens (if used) can expose them to XSS.
Another misconfiguration involves improper scope or permission checks after token validation. A token may be considered valid, but the associated user lacks permission for a specific endpoint. Without explicit scope or role checks, this leads to Insecure Direct Object References (IDOR) or Broken Level of Authorization (BOLA). Additionally, storing tokens in local storage or debug logs increases exposure risk, especially if error responses inadvertently include authorization headers.
Django REST framework (DRF) provides built-in token authentication classes, but if developers use TokenAuthentication without enforcing HTTPS or without rate limiting, tokens can be brute-forced or replayed. Misconfigured CORS settings can also allow unauthorized origins to send authenticated requests with bearer tokens, leading to cross-origin data exposure.
To detect these issues, scanners run checks for missing transport security, improper token storage practices, missing scope enforcement, and inconsistent authentication across endpoints. These checks help identify whether token-based authentication is implemented securely within the Django application.
Bearer Tokens-Specific Remediation in Django — concrete code fixes
Remediation focuses on ensuring tokens are transmitted and validated securely, with proper transport protection and strict permission checks. Always enforce HTTPS using Django’s SECURE_SSL_REDIRECT and HSTS settings, and validate tokens over secure channels only.
Use Django REST framework’s token authentication with explicit HTTPS enforcement and scope-based permissions. Below is a secure example using token authentication with HTTPS redirection and scope validation.
from rest_framework.authentication import TokenAuthentication
from rest_framework.permissions import BasePermission
from django.conf import settings
class TokenHasScope(BasePermission):
def __init__(self, scopes):
self.scopes = scopes
def has_permission(self, request, view):
auth = TokenAuthentication()
try:
auth.authenticate(request)
except Exception:
return False
# Example: ensure token has required scope
token = request.auth
user_scopes = getattr(token.user, 'scopes', [])
return all(scope in user_scopes for scope in self.scopes)
# In views.py
from rest_framework.views import APIView
from rest_framework.response import Response
class SecureEndpoint(APIView):
authentication_classes = [TokenAuthentication]
permission_classes = [TokenHasScope]
def get(self, request):
return Response({'message': 'secure data'})
# settings.py
SECURE_SSL_REDIRECT = True
SESSION_COOKIE_SECURE = True
CSRF_COOKIE_SECURE = True
SECURE_HSTS_SECONDS = 31536000
SECURE_HSTS_INCLUDE_SUBDOMAINS = True
For custom header-based token validation (e.g., Authorization: Bearer <token>), implement a custom authentication class that validates the token against your identity provider and enforces scope checks before allowing access.
from rest_framework.authentication import BaseAuthentication
from rest_framework.exceptions import AuthenticationFailed
from django.contrib.auth.models import User
class BearerTokenAuthentication(BaseAuthentication):
def authenticate(self, request):
auth_header = request.headers.get('Authorization')
if not auth_header or not auth_header.startswith('Bearer '):
return None
token = auth_header.split(' ')[1]
# Validate token with your backend (e.g., introspection endpoint)
user = self.validate_token(token)
if not user:
raise AuthenticationFailed('Invalid token')
return (user, token)
def validate_token(self, token):
# Example: call OAuth introspection endpoint
import requests
resp = requests.post(
'https://auth.example.com/introspect',
data={'token': token},
auth=('client_id', 'client_secret'),
timeout=5
)
if resp.status_code == 200 and resp.json().get('active'):
username = resp.json().get('username')
try:
return User.objects.get(username=username)
except User.DoesNotExist:
return None
return None
Ensure that tokens are not logged by customizing logging filters to strip Authorization headers and by avoiding debug tools in production. Apply CORS restrictions tightly using django-cors-headers to limit origins that can present bearer tokens.