Api Key Exposure in Django with Bearer Tokens
Api Key Exposure in Django with Bearer Tokens — how this specific combination creates or exposes the vulnerability
Bearer tokens are commonly used for HTTP API authentication, and in Django they are often passed via the Authorization header as Authorization: Bearer <token>. When improperly handled, this pattern can lead to Api Key Exposure in Django applications. A typical misconfiguration is relying solely on client-side storage or insecure transmission, which can expose the token in logs, browser developer tools, or through insecure referrer headers.
In Django, developers sometimes store tokens in settings or environment variables but inadvertently expose them through error messages, debug pages, or misconfigured middleware. For example, if an API endpoint returns a detailed error that includes the Authorization header value, a token may be leaked in clear text. Similarly, logging configurations that capture request headers without filtering sensitive fields can persist tokens in application logs, creating a long-term exposure risk.
Another common vulnerability arises when Django views or decorators pass the Authorization header directly to downstream services or third-party APIs without stripping or sanitizing it. This can occur in proxy-like behavior where the token is forwarded unintentionally, enabling an attacker who compromises a downstream service to obtain valid credentials. Additionally, if a Django application embeds tokens in JavaScript or HTML responses, they may be accessible through client-side code execution or inspection, which is particularly dangerous when Content Security Policy (CSP) is not properly enforced.
Tokens may also be exposed through insecure transport if HTTPS is not enforced consistently across the application. While Django’s SECURE_SSL_REDIRECT can help, mixed content or misconfigured load balancers may allow token transmission over unencrypted channels. Furthermore, if session-based authentication is used in conjunction with Bearer tokens without proper isolation, cross-contamination of credentials can occur, especially when cookies and tokens share the same scope.
To detect such exposure, scanning tools like middleBrick analyze runtime behavior and OpenAPI specifications to identify whether tokens appear in logs, error responses, or uncontrolled outbound calls. The tool checks for missing security headers, improper error handling, and insecure transmission practices that could lead to token leakage. By correlating specification definitions with observed responses, it highlights scenarios where Bearer tokens are unnecessarily echoed or proxied without safeguards.
Bearer Tokens-Specific Remediation in Django — concrete code fixes
Remediation focuses on ensuring Bearer tokens are never logged, echoed, or forwarded unintentionally, and are always transmitted over encrypted channels. Below are concrete code examples demonstrating secure handling in Django.
1. Secure Authorization Header Parsing
Always extract the token server-side without exposing it in error messages or logs. Use a dedicated middleware to sanitize headers before they reach views.
import re
from django.utils.deprecation import MiddlewareMixin
class BearerTokenSanitizationMiddleware(MiddlewareMixin):
def process_request(self, request):
auth = request.META.get('HTTP_AUTHORIZATION', '')
# Extract token without logging full header
if auth.startswith('Bearer '):
token = auth[7:]
# Store token in request for secure downstream use
request.token = token
else:
request.token = None
# Ensure original header is not passed to views by default
request.META['HTTP_AUTHORIZATION'] = 'REDACTED' if auth else ''
2. Secure API View with Token Validation
Use token validation in views while avoiding inclusion of the token in any response or error output.
from django.http import JsonResponse
from django.views import View
from django.core.exceptions import PermissionDenied
class SecureApiView(View):
def get(self, request):
token = getattr(request, 'token', None)
if not token or not self.is_valid_token(token):
raise PermissionDenied('Invalid token')
# Perform action without referencing token in response
return JsonResponse({'status': 'ok'})
def is_valid_token(self, token):
# Implement token validation logic (e.g., against database or external auth service)
return token == 'expected_secure_token_value'
3. Logging Configuration to Prevent Token Exposure
Configure logging to filter Authorization headers before they are written to logs.
import logging
class FilterSensitiveHeaders(logging.Filter):
def filter(self, record):
if hasattr(record, 'msg'):
record.msg = re.sub(r'(Authorization:\s*Bearer\s*)\S+', r'\1***REDACTED***', record.msg)
return True
logger = logging.getLogger('django.request')
logger.addFilter(FilterSensitiveHeaders())
4. Enforce HTTPS and Secure Transmission
Ensure all API interactions occur over HTTPS and redirect HTTP traffic.
# settings.py
SECURE_SSL_REDIRECT = True
SESSION_COOKIE_SECURE = True
CSRF_COOKIE_SECURE = True
SECURE_HSTS_SECONDS = 31536000
SECURE_HSTS_INCLUDE_SUBDOMAINS = True
5. Avoid Token Echo in Error Responses
Customize error handling to prevent inclusion of sensitive headers in debug output.
from django.views.decorators.debug import sensitive_post_parameters
@sensitive_post_parameters('Authorization')
def my_api_view(request):
# Your logic here
pass