HIGH cache poisoningdjangopython

Cache Poisoning in Django (Python)

Cache Poisoning in Django with Python — how this specific combination creates or exposes the vulnerability

Cache poisoning in Django with Python occurs when an attacker causes the application to store malicious or incorrect data in the cache, which is then served to other users. Because Django often caches rendered pages, fragments, or computed values using Python-based cache backends (e.g., Memcached, Redis, or local memory), improperly validated or user-influenced cache keys and values can lead to security issues.

Django’s cache framework is configured in settings.py and typically integrated into views using decorators like @cache_page or the low-level API cache.set / cache.get. If cache keys incorporate user-controlled input without normalization or escaping, two users with different parameters might receive each other’s cached content. For example, a URL like /search?q=django might be cached under a key derived directly from query parameters, enabling an attacker to poison the cache for a benign query and later trigger it via a crafted parameter that leads to XSS or sensitive data exposure.

In Python, cache poisoning can also arise from unsafe serialization. Django’s file-based and database cache backends serialize values using Python’s pickle by default in some configurations, which allows arbitrary code execution if an attacker can force deserialization of malicious payloads. Even when using safer backends like Redis, storing user-derived strings without canonicalization can cause different inputs (e.g., user_id=1 vs user_id=01) to map to the same cache entry, leading to privilege escalation or data confusion.

Real-world patterns include caching responses that include user identifiers or roles without validating scope, enabling horizontal or vertical privilege escalation via BOLA/IDOR. For instance, caching a profile page keyed by user ID without ensuring the requester owns that ID means an authenticated user might see another user’s cached data. OWASP API Top 10 categories such as Broken Object Level Authorization (BOLA) and Security Misconfiguration map to these risks. While middleBrick scans detect such misconfigurations and provide prioritized findings with remediation guidance, developers must ensure cache keys are deterministic, scoped, and validated, and avoid unsafe deserialization practices.

To illustrate, consider a vulnerable Django view that caches search results using raw query parameters:

from django.views.decorators.cache import cache_page

@cache_page(60 * 15)  # cache for 15 minutes
def search_view(request):
    query = request.GET.get('q', '')
    # Unsafe: cache key directly derived from user input
    results = perform_search(query)
    return render(request, 'search.html', {'results': results, 'query': query})

An attacker can supply parameters that alter the cache key in unintended ways, leading to cache poisoning or data leakage across users.

Python-Specific Remediation in Django — concrete code fixes

Remediation focuses on normalizing inputs, isolating cache namespaces, and avoiding unsafe deserialization. In Python-based Django caching, always sanitize and scope cache keys, prefer safe serialization formats, and validate authorization before serving cached content.

1. Scope cache keys with user or tenant context
Use the request’s user ID or tenant ID as part of the cache key to prevent cross-user pollution. Combine static namespace strings with validated identifiers:

from django.core.cache import cache

def get_search_results(query, user_id):
    # Safe: include user_id in cache key to isolate per user
    cache_key = f'search:v2:user:{user_id}:q:{hashlib.sha256(query.encode()).hexdigest()}'
    result = cache.get(cache_key)
    if result is None:
        result = perform_search(query)
        cache.set(cache_key, result, timeout=60 * 15)
    return result

2. Avoid pickle-based serialization for shared caches
Ensure your cache backend does not rely on Python pickle for shared or external caches. In Redis, configure Django to use a JSON serializer for safety:

CACHES = {
    'default': {
        'BACKEND': 'django_redis.cache.RedisCache',
        'LOCATION': 'redis://127.0.0.1:6379/1',
        'OPTIONS': {
            'CLIENT_CLASS': 'django_redis.client.DefaultClient',
            'SERIALIZER': 'django_redis.serializers.json.JSONSerializer',
        },
    }
}

3. Normalize and validate inputs before caching
Canonicalize query parameters and enforce strict validation to avoid equivalent-but-different keys:

import re

def normalize_query_param(value):
    # Remove extra whitespace and enforce lowercase where appropriate
    if not isinstance(value, str):
        return ''
    return re.sub(r'\s+', ' ', value.strip().lower())

query = normalize_query_param(request.GET.get('q', ''))
if not query:
    raise ValidationError('Invalid query')

4. Use per-view or per-user fragment caching with proper vary headers
When using template fragment caching, include request headers that reflect user context via vary headers:

from django.views.decorators.vary import vary_headers

@cache_page(60 * 15, key_prefix='public')
@vary_headers('Cookie')
def profile_view(request):
    # Cache varies by Cookie, reducing cross-user risk
    return render(request, 'profile.html')

5. Audit cache reads/writes with logging and testing
Instrument cache operations in Python to detect anomalies and validate that cache keys remain bounded and non-overlapping across privilege levels.

By combining these Python-specific practices with active scanning using tools like middleBrick, teams can detect cache poisoning risks early. The dashboard tracks scoring trends, the CLI integrates into scripts for JSON output, and the GitHub Action can fail builds if risk thresholds are exceeded, while the MCP Server enables AI coding assistants to surface issues during development.

Frequently Asked Questions

How can I test if my Django cache is vulnerable to poisoning?
Send requests with differing but equivalent cache keys (e.g., varied whitespace or encoding in query parameters) and check whether responses differ. Use the middleBrick CLI to scan the endpoint and review findings related to BOLA/IDOR and input validation.
Does using Django’s cache framework automatically protect against poisoning?
No. The framework provides caching primitives but does not enforce key scoping or input validation. Developers must design cache keys carefully, avoid unsafe deserialization, and validate user-controlled inputs, supported by scans from the middleBrick dashboard or CLI.