Memory Leak in Django with Bearer Tokens
Memory Leak in Django with Bearer Tokens — how this specific combination creates or exposes the vulnerability
A memory leak in a Django service that uses Bearer token authentication typically arises when token handling, request processing, and session-like state are not carefully managed. Because Bearer tokens are often stored in memory per-request or per-worker (for example in caches or global dictionaries keyed by token), references can accumulate unintentionally, preventing Python’s garbage collector from reclaiming memory. This is especially relevant in long-running processes where each authenticated request adds a small but persistent object, gradually increasing the process footprint.
Consider a common pattern: a middleware or view that caches user scopes or parsed token payloads in a module-level dictionary keyed by the token string. If tokens carry large claims, or if the cache lacks eviction, each token remains referenced for the lifetime of the worker. In a multi-threaded or async worker, this can cause steady growth in resident memory. Additionally, if token validation logic creates heavy objects (e.g., large JSON deserializations or ORM query sets attached to the token context), and these are retained beyond the request lifecycle, the leak is amplified.
Django’s interaction with Bearer tokens can also expose leaks in integration libraries. For example, misconfigured token verification that repeats expensive operations per request, or stores results in global structures to avoid recomputation, can turn a small overhead into a persistent accumulation. In environments with many unique tokens (such as microservice APIs), this manifests as gradual slowdowns and increased memory usage, which may be flagged by middleBrick’s property authorization and input validation checks as anomalous runtime behavior.
From a scanning perspective, middleBrick’s 12 security checks run in parallel and can surface indicators that suggest memory stress or insecure token handling, such as unusual process behavior or missing authorization constraints on token-consuming endpoints. While the scanner does not inspect Python memory internals, it can detect insecure patterns like missing scope validation (BOLA/IDOR), missing rate limits, or improper input validation that can contribute to resource misuse when tokens are processed inefficiently.
Remediation guidance centers on ensuring token processing is stateless and bounded: avoid caching token payloads in long-lived structures, prefer lightweight token introspection, and enforce strict authorization checks per endpoint. Leveraging standardized validation libraries and keeping token payloads small reduces the chance of retention. If continuous monitoring is in place, teams can correlate rising memory metrics with specific token-heavy endpoints identified in middleBrick reports.
Bearer Tokens-Specific Remediation in Django — concrete code fixes
To mitigate memory leaks when using Bearer tokens in Django, focus on stateless token handling, bounded caches, and disciplined request cleanup. Below are concrete patterns and examples.
1. Avoid global token caches
Do not store token payloads or user data in module-level dictionaries. Instead, keep token validation per-request and avoid retaining references after the response is sent.
# BAD: module-level cache that can grow indefinitely
TOKEN_CACHE = {}
def validate_token(token):
if token in TOKEN_CACHE:
return TOKEN_CACHE[token]
# expensive validation...
payload = decode_and_verify(token)
TOKEN_CACHE[token] = payload # memory leak
return payload
# BETTER: validate each request without persistent cache
def validate_token(token):
return decode_and_verify(token) # stateless
2. Use Django’s authentication with short-lived tokens
Configure token-based authentication to avoid long-lived in-memory sessions. Use token rotation and short expiry to reduce the window for accumulation.
# settings.py
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': [
'rest_framework.authentication.TokenAuthentication',
],
'UNAUTHENTICATED_USER': None,
}
# Token model with expiry
from django.db import models
from django.contrib.auth.models import User
class UserToken(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
key = models.CharField(max_length=40, unique=True)
expires_at = models.DateTimeField()
# Ensure periodic cleanup of expired tokens
3. Streamline token parsing in views and middleware
Parse and authorize tokens per request without attaching heavy objects to the request in a way that persists beyond the response lifecycle.
import jwt
from django.http import JsonResponse
from django.utils.deprecation import MiddlewareMixin
class BearerTokenMiddleware(MiddlewareMixin):
def process_request(self, request):
auth = request.META.get('HTTP_AUTHORIZATION', '')
if auth.startswith('Bearer '):
token = auth.split(' ')[1]
# Lightweight decode, avoid caching large payloads
try:
payload = jwt.decode(token, options={'verify_signature': False})
request.user_scope = payload.get('scope') # keep minimal
except jwt.PyJWTError:
request.user_scope = None
else:
request.user_scope = None
def process_response(self, request, response):
# Ensure no large objects linger on request
if hasattr(request, 'user_scope'):
del request.user_scope
return response
def api_protected_view(request):
scope = getattr(request, 'user_scope', None)
if scope != 'read:data':
return JsonResponse({'error': 'forbidden'}, status=403)
return JsonResponse({'data': 'ok'})
4. Enforce rate limiting and scope checks
Combine token validation with rate limiting to reduce abuse and resource exhaustion. Use Django middleware or third-party packages to keep per-token usage bounded.
from django.core.cache import cache
def rate_limited_view(request):
scope = getattr(request, 'user_scope', None)
if not scope:
return JsonResponse({'error': 'unauthorized'}, status=401)
key = f"rate:{scope}:{request.META.get('REMOTE_ADDR')}"
current = cache.get(key, 0)
if current > 100:
return JsonResponse({'error': 'rate limit'}, status=429)
cache.set(key, current + 1, timeout=60)
return JsonResponse({'data': 'success'})
5. Periodic cleanup and monitoring
Even with careful code, some retention can occur. Implement periodic tasks to clear caches and monitor memory growth, correlating findings from middleBrick scans to identify endpoints with unusual token handling patterns.
# Example management command (management/commands/clean_token_cache.py)
from django.core.management.base import BaseCommand
import gc
class Command(BaseCommand):
def handle(self, **options):
# If using any caches, clear or prune here
gc.collect()
self.stdout.write('Cleaned token-related caches and collected garbage.')
By adopting these patterns—stateless validation, bounded caches, minimal payload retention, and regular cleanup—you reduce the risk of memory leaks in Django services that rely on Bearer tokens. middleBrick’s checks for authentication, input validation, and property authorization can help surface risky endpoints where token handling may contribute to resource anomalies.