HIGH token leakagedjangobearer tokens

Token Leakage in Django with Bearer Tokens

Token Leakage in Django with Bearer Tokens — how this specific combination creates or exposes the vulnerability

Token leakage in Django when using Bearer Tokens occurs when authentication tokens are exposed beyond their intended scope, typically through logging, error messages, insecure storage, or misconfigured transport. In Django, developers commonly use token-based authentication via packages such as djangorestframework-simplejwt or custom token implementations to issue Bearer access and refresh tokens. While these packages provide secure defaults, improper usage can expose these tokens in server logs, browser console JavaScript errors, or network traces, enabling session hijacking.

Bearer Tokens are high-sensitivity credentials: if leaked, an attacker can impersonate the associated user without needing the user’s password. Common leakage vectors in Django include:

  • Inclusion of the full Authorization header in application or web server logs due to overly verbose logging configurations.
  • Token exposure in HTTP Referer headers when making cross-origin requests from browser JavaScript.
  • Storage of tokens in local storage or session storage in the browser, which are accessible via JavaScript and vulnerable to cross-site scripting (XSS).
  • Tokens returned in HTTP responses or error payloads when API error handling is not carefully designed to strip sensitive data.

Consider a typical Django REST Framework view that authenticates using a Bearer Token:

from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.permissions import IsAuthenticated

class UserProfileView(APIView):
    permission_classes = [IsAuthenticated]

    def get(self, request):
        # request.auth is the token payload decoded from the Bearer token
        return Response({
            'user_id': str(request.auth['user_id']),
            'scope': request.auth.get('scope', 'read')
        })

If this view raises an unhandled exception and the Django debug page is enabled in production, or if logging inadvertently captures the full request headers, the Authorization header containing the Bearer token can be recorded. Similarly, if the frontend JavaScript makes authenticated requests and logs the request configuration, the token can be printed to the browser console.

Django settings also influence leakage risk. For example, using DEBUG = True in production can expose stack traces that include request metadata. Even without DEBUG, misconfigured middleware or third-party packages might inspect or mirror headers, inadvertently exposing Authorization values. Because Bearer Tokens are bearer-based (possession proof), any party that observes the token can use it until it expires or is revoked, making leakage particularly dangerous compared to other schemes that require client secrets.

SSRF and external service integrations can compound leakage: if your Django backend forwards requests to external endpoints and includes the Authorization header, a misconfigured or malicious upstream service might log or reflect the token. This ties into the broader API security checks provided by tools like middleBrick, which can detect instances where authentication headers appear in unauthenticated attack surface tests and highlight risks such as BOLA/IDOR or unsafe consumption patterns that may facilitate token leakage.

Bearer Tokens-Specific Remediation in Django — concrete code fixes

Remediation focuses on preventing token exposure in logs, browser contexts, and error responses while ensuring tokens are handled with minimal privilege and short lifetimes. Below are concrete, safe patterns for Django projects.

1. Avoid logging Authorization headers

Configure logging filters to redact sensitive headers. In settings.py, add a filter that scrubs the Authorization header from any log record that includes request data:

import logging

class RedactAuthFilter(logging.Filter):
    def filter(self, record):
        if hasattr(record, 'request') and hasattr(record.request, 'META'):
            headers = record.request.META
            if 'HTTP_AUTHORIZATION' in headers:
                headers['HTTP_AUTHIZATION'] = '[REDACTED]'
        return True

LOGGING = {
    'version': 1,
    'filters': {
        'redact_auth': {
            '()': RedactAuthFilter,
        }
    },
    'handlers': {
        'console': {
            'class': 'logging.StreamHandler',
            'filters': ['redact_auth'],
        }
    },
    'loggers': {
        'django.request': {
            'handlers': ['console'],
            'level': 'WARNING',
        }
    }
}

2. Serve frontend assets without exposing tokens to JavaScript

Never store Bearer tokens in localStorage or sessionStorage. Instead, use HttpOnly, Secure, SameSite cookies for refresh tokens and keep access tokens short-lived and delivered via the Authorization header only. Configure CORS and CSRF carefully:

# settings.py
CSRF_COOKIE_SAMESITE = 'Strict'
CSRF_COOKIE_SECURE = True
SESSION_COOKIE_SAMESITE = 'Strict'
SESSION_COOKIE_SECURE = True
CSRF_USE_SESSIONS = True

For API calls from JavaScript, rely on cookie-based session handling where the browser automatically attaches cookies, and ensure your views validate the CSRF token when appropriate. If you must use token-based auth in SPAs, keep tokens in memory only and avoid including them in URLs or logs.

3. Strip sensitive data from error responses

Customize exception handling to avoid exposing token details. In your project’s views.py or a dedicated exception handler (if using DRF):

from rest_framework.views import exception_handler

def custom_exception_handler(exc, context):
    response = exception_handler(exc, context)
    if response is not None and 'detail' in response.data:
        # Generic message to avoid leaking token context
        if 'token' in str(response.data['detail']).lower():
            response.data['detail'] = 'Authentication error.'
    return response

Ensure DEBUG = False in production and that your web server (e.g., Nginx) does not proxy error pages that might echo headers or tokens.

4. Use short-lived access tokens and secure refresh flows

Configure token lifetimes to minimize the impact of leakage. With djangorestframework-simplejwt:

from datetime import timedelta

SIMPLE_JWT = {
    'ACCESS_TOKEN_LIFETIME': timedelta(minutes=15),
    'REFRESH_TOKEN_LIFETIME': timedelta(days=1),
    'ROTATE_REFRESH_TOKENS': True,
    'BLACKLIST_AFTER_ROTATION': True,
}

This ensures that leaked access tokens expire quickly and refresh tokens are rotated, reducing the window for abuse. Pair this with secure HTTPS transport and strict validation of token audiences and issuers.

5. MiddleBrick and continuous scanning

Using the middleBrick CLI, you can regularly scan your Django endpoints to detect whether Authorization headers appear in unauthenticated scans or whether findings such as BOLA/IDOR or Unsafe Consumption could facilitate token leakage:

# Scan from terminal with middlebrick
middlebrick scan https://api.example.com/openapi.json

For teams with more mature workflows, the middleBrick Pro plan enables continuous monitoring and CI/CD integration so that regressions in authentication exposure are caught before deployment. The GitHub Action can fail builds if risk scores drop below your defined threshold, and the MCP Server allows you to scan APIs directly from your AI coding assistant within the IDE.

Frequently Asked Questions

What should I do if I see an Authorization header in my server logs?
Immediately rotate all issued Bearer tokens, audit your logging configuration to redact Authorization headers, and verify that DEBUG is set to False in production. Use the remediation steps above to prevent future leakage.
Is storing Bearer tokens in local storage safe if I use short expiration times?
No. Local storage is accessible via JavaScript and vulnerable to XSS. Prefer HttpOnly, Secure cookies for refresh tokens and keep access tokens short-lived delivered via the Authorization header only.