Data Exposure in Django with Bearer Tokens
Data Exposure in Django with Bearer Tokens
Bearer tokens are commonly used to protect APIs by requiring a token in the Authorization header. In Django, if token validation is incomplete or tokens are handled carelessly, sensitive data can be exposed. A typical misconfiguration occurs when views or middleware do not properly restrict access to endpoints, allowing an unauthenticated or low-privilege actor to read or intercept token-protected resources.
Data exposure with Bearer tokens in Django can happen when responses include sensitive fields (e.g., email, internal IDs, tokens) in JSON serialization, or when debug settings inadvertently leak information in error pages. For example, if a Django REST Framework (DRF) serializer does not explicitly exclude sensitive attributes, those fields may be returned to any client that presents a valid token obtained through other means. This violates the principle of least privilege and can lead to account enumeration or data exfiltration.
Another exposure path involves logging. If Bearer tokens or user identifiers are written to application logs without redaction, tokens can be persisted in log files and later accessed by unauthorized parties. Similarly, improper CORS settings can allow browser-based JavaScript to make authenticated requests to your Django backend from unintended origins, expanding the exposure surface beyond intended clients.
Middleware that does not validate token scope or audience can also contribute to data exposure. A token issued for one API or service might be accepted by another endpoint within the same Django project if scope checks are omitted. This becomes critical when token introspection or revocation mechanisms are not implemented, as a stolen token may remain usable for extended periods.
middleBrick scans for these classes of risk under Data Exposure by correlating runtime behavior with OpenAPI specifications, including how authentication schemes are defined and which endpoints require authorization. It checks whether responses inadvertently include sensitive data structures and whether security headers that mitigate exposure are present. These checks are performed without requiring authentication, focusing on the unauthenticated attack surface to identify endpoints that should be protected but are not.
Bearer Tokens-Specific Remediation in Django
Remediation focuses on strict token validation, minimizing data in responses, and hardening logging and transport settings. Below are concrete patterns you can apply in Django and Django REST Framework projects.
1. Use DRF’s TokenAuthentication with explicit permission classes
Ensure that views requiring authentication explicitly declare permission classes and that token validation is enforced. Avoid allowing unsafe methods for anonymous users.
from rest_framework.permissions import IsAuthenticated
from rest_framework.authentication import TokenAuthentication
from rest_framework.views import APIView
from rest_framework.response import Response
class UserProfileView(APIView):
authentication_classes = [TokenAuthentication]
permission_classes = [IsAuthenticated]
def get(self, request):
# Only authenticated requests with a valid token reach here
return Response({
'id': request.user.id,
'username': request.user.username,
# Never include tokens or passwords in the response
})
2. Customize token scope and validate audience
If you use a custom token model, add scope validation to ensure a token is only valid for intended endpoints. This prevents token reuse across services.
from rest_framework_simplejwt.tokens import Token
class CustomToken(Token):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# Example: embed scope claim
self.payload['scope'] = 'api:read'
def validate_token_scope(token, required_scope):
scope = token.payload.get('scope')
if scope != required_scope:
raise PermissionDenied('Invalid token scope')
return True
3. Serialize with explicit fields and exclude sensitive data
Define serializers that list only necessary fields. Avoid using fields = '__all__' when user data is exposed.
from rest_framework import serializers
from django.contrib.auth import get_user_model
User = get_user_model()
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ['id', 'username', 'email'] # Explicitly declare safe fields
# Exclude fields like 'password' or 'auth_token' automatically
4. Redact tokens in logging configuration
Update your logging configuration to filter out tokens from log messages. Use a custom filter to prevent accidental exposure.
import logging
class TokenFilter(logging.Filter):
def filter(self, record):
if hasattr(record, 'msg'):
# Basic redaction pattern for bearer tokens in log messages
if isinstance(record.msg, str) and 'Bearer ' in record.msg:
record.msg = '[REDACTED]'
return True
logger = logging.getLogger()
logger.addFilter(TokenFilter())
5. Enforce HTTPS and secure headers
Set security headers and enforce HTTPS to prevent token interception in transit. Use Django middleware and settings to control transport security.
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
6. Validate token origin and implement revocation
Where applicable, validate token origin and maintain a revocation list. Combine short-lived access tokens with refresh tokens to limit exposure if a token is compromised.
# Example of checking a token against a denylist (pseudocode)
from django.core.cache import cache
def is_token_revoked(token_jti):
return cache.get(f'token_revoked_{token_jti}', False)
Related CWEs: dataExposure
| CWE ID | Name | Severity |
|---|---|---|
| CWE-200 | Exposure of Sensitive Information | HIGH |
| CWE-209 | Error Information Disclosure | MEDIUM |
| CWE-213 | Exposure of Sensitive Information Due to Incompatible Policies | HIGH |
| CWE-215 | Insertion of Sensitive Information Into Debugging Code | MEDIUM |
| CWE-312 | Cleartext Storage of Sensitive Information | HIGH |
| CWE-359 | Exposure of Private Personal Information (PII) | HIGH |
| CWE-522 | Insufficiently Protected Credentials | CRITICAL |
| CWE-532 | Insertion of Sensitive Information into Log File | MEDIUM |
| CWE-538 | Insertion of Sensitive Information into Externally-Accessible File | HIGH |
| CWE-540 | Inclusion of Sensitive Information in Source Code | HIGH |