Pii Leakage in Django with Bearer Tokens
Pii Leakage in Django with Bearer Tokens — how this specific combination creates or exposes the vulnerability
In Django, PII leakage with Bearer Tokens often occurs when token handling, storage, and transmission are not tightly controlled. A Bearer Token is a simple string that grants access to the resource owner; if mishandled, it can expose sensitive information or allow unauthorized access to PII.
Developers sometimes store tokens in logs, URLs, or browser storage without adequate safeguards. For example, logging an authorization header in Django can inadvertently capture and persist Bearer Tokens alongside request metadata, creating a searchable record that may include user identifiers or other PII. If those logs are accessible to unauthorized parties, the tokens and the data they protect can be exposed.
Another common pattern is passing tokens in query strings rather than in HTTP headers. URLs are often stored in server logs, browser history, and referrer headers. A URL like /api/profile?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9... can expose the token and any associated PII to anyone with access to those logs. Additionally, if Django responses include the Authorization header in error messages or debug output, tokens may be disclosed through unhandled exceptions or the Django debug page in production.
PII leakage can also stem from insufficient token validation and scope enforcement. If a Bearer Token is accepted without verifying its scopes or audience, an attacker might use a token intended for one service to access another service that returns PII. Inadequate token revocation mechanisms compound the risk: a compromised token can continue to grant access to protected endpoints, allowing extraction of user data over time.
The interaction between Django’s middleware and token-based authentication can introduce further risks. For instance, if authentication middleware attaches user details to the request object based on an unverified or loosely validated token, subsequent processing or logging may expose PII tied to that user. Cross-site request forgery (CSRF) protections may be mistakenly disabled for token-based endpoints, enabling unauthorized actions that exfiltrate data.
To detect these issues, scanning tools evaluate whether tokens are transmitted securely, stored safely, and validated rigorously. They check whether tokens appear in logs, URLs, or error messages, and whether Django settings inadvertently expose sensitive information. Effective security testing maps token handling paths and identifies where PII could be inadvertently exposed through token misuse or misconfiguration.
Bearer Tokens-Specific Remediation in Django — concrete code fixes
Remediation focuses on secure transmission, storage, and validation of Bearer Tokens within Django. Always transmit tokens via the Authorization header using the Bearer scheme, never in URLs or form fields.
import requests
# Secure: Send token in Authorization header
token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
headers = {
"Authorization": f"Bearer {token}",
"Content-Type": "application/json"
}
response = requests.get("https://api.example.com/profile", headers=headers)
On the server side, ensure token validation is strict. Use Django REST framework’s permission classes and token authentication utilities to verify token integrity and scope before processing requests.
from rest_framework.authentication import TokenAuthentication
from rest_framework.permissions import IsAuthenticated
from rest_framework.views import APIView
from rest_framework.response import Response
class SecureProfileView(APIView):
authentication_classes = [TokenAuthentication]
permission_classes = [IsAuthenticated]
def get(self, request):
# Token is validated by TokenAuthentication
user = request.user
# Ensure only necessary, non-sensitive data is returned
return Response({
"user_id": user.id,
"username": user.username,
# Avoid including PII such as email or phone unless strictly required
})
Configure Django settings to minimize exposure. Disable the debug mode in production and avoid logging sensitive headers. Use middleware to scrub tokens from logs and error reports.
# settings.py
import os
DEBUG = False
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'filters': {
'require_debug_false': {
'()': 'django.utils.log.RequireDebugFalse',
},
'token_scrubber': {
'()': 'myapp.logging.TokenScrubberFilter',
},
},
'handlers': {
'console': {
'level': 'INFO',
'filters': ['require_debug_false', 'token_scrubber'],
'class': 'logging.StreamHandler',
},
},
'loggers': {
'django.request': {
'handlers': ['console'],
'level': 'ERROR',
'propagate': True,
},
},
}
Implement a custom logging filter to remove or mask Bearer Tokens from any log entries. This prevents tokens from being persisted alongside request details.
# myapp/logging.py
import re
class TokenScrubberFilter:
# Basic pattern for Bearer tokens; tailor to your token format
BEARER_PATTERN = re.compile(r'Bearer [A-Za-z0-9\-_=]+\.[A-Za-z0-9\-_=]+[.A-Za-z0-9\-_=]*')
def filter(self, record):
if hasattr(record, 'msg'):
record.msg = self.BEARER_PATTERN.sub('[FILTERED_TOKEN]', record.msg)
if hasattr(record, 'args') and isinstance(record.args, tuple):
record.args = tuple(
self.BEARER_PATTERN.sub('[FILTERED_TOKEN]', str(arg)) if isinstance(arg, str) else arg
for arg in record.args
)
return True
Enforce token scope and audience checks to prevent privilege escalation and ensure tokens are used only for intended purposes. Validate issuers and expiration rigorously.
# myapp/auth.py
import jwt
from django.conf import settings
from django.core.exceptions import PermissionDenied
def validate_token_scope(token, required_scope):
try:
decoded = jwt.decode(
token,
settings.SECRET_KEY,
algorithms=["HS256"],
audience="my-api-audience",
options={"verify_exp": True, "require_scope": True}
)
if required_scope not in decoded.get("scope", "").split():
raise PermissionDenied("Insufficient scope")
return decoded
except jwt.ExpiredSignatureError:
raise PermissionDenied("Token expired")
except jwt.InvalidTokenError:
raise PermissionDenied("Invalid token")
Ensure tokens are invalidated after logout or compromise by maintaining a denylist with expiry awareness. Combine short token lifetimes with refresh mechanisms where appropriate.
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 |