HIGH cache poisoningdjango

Cache Poisoning in Django

How Cache Poisoning Manifests in Django

Cache poisoning in Django occurs when malicious input manipulates cache keys or cached responses, causing the server to serve incorrect or sensitive data to other users. Django's caching framework, while powerful, can become a vector for attacks if not properly secured.

The most common Django-specific cache poisoning pattern involves user input directly influencing cache keys. Consider this vulnerable view:

from django.views.decorators.cache import cache_page
from django.http import JsonResponse

def user_profile(request, username):
    # Vulnerable: username directly used in cache key
    @cache_page(60 * 60)
    def cached_view():
        user = get_object_or_404(User, username=username)
        return JsonResponse({
            'username': user.username,
            'email': user.email,
            'bio': user.profile.bio
        })
    
    return cached_view()

An attacker can craft usernames with special characters or predictable patterns to manipulate cache keys. For example, using usernames like admin/../ or exploiting case sensitivity in cache backends could cause cache collisions where one user's data is served to another.

Django's template caching also presents risks. When using {% cache %} template tags with user input:

{% load cache %}
{% cache 500 user_content request.user.id %}
    <div class="user-content">
        <p>{{ user.content|safe }}</p>
    </div>
{% endcache %}

If user.content contains HTML or JavaScript, and the cache isn't properly segregated per user, one user might see another's content due to cache key collisions or improper cache invalidation.

Another Django-specific scenario involves cache poisoning through querystring manipulation. Django's default cache key generation includes the full request path:

def search_view(request):
    query = request.GET.get('q', '')
    page = request.GET.get('page', 1)
    
    # Cache key includes full path: /search/?q=test&page=1
    results = cache.get(f'search_results:{query}:{page}')
    
    if results is None:
        results = perform_search(query, page)
        cache.set(f'search_results:{query}:{page}', results, 300)
    
    return render(request, 'search.html', {'results': results})

An attacker could manipulate querystring parameters to create cache collisions or poison the cache with malicious content that gets served to other users searching for similar terms.

Django-Specific Detection

Detecting cache poisoning in Django requires examining both code patterns and runtime behavior. middleBrick's Django-specific scanning identifies these vulnerabilities through several mechanisms.

Code analysis focuses on finding cache decorators and template tags that use user input directly. middleBrick scans for patterns like:

@cache_page(timeout, key_prefix=user_input)
@cache_control(...)
{% cache timeout user_input %}

The scanner checks if cache keys incorporate request parameters, session data, or other user-controlled values without proper sanitization. It also examines cache key generation logic for predictable patterns that could lead to collisions.

Runtime detection involves active probing of cache behavior. middleBrick tests cache endpoints by:

  • Sending requests with manipulated cache keys to observe collision behavior
  • Checking for cross-user data leakage in cached responses
  • Testing cache invalidation timing to see if stale data persists
  • Verifying that cache segregation works properly between authenticated users

For Django applications using Redis or Memcached, middleBrick can detect misconfigurations where cache namespaces aren't properly isolated. The scanner checks for:

# Vulnerable: shared cache namespace
cache = caches['default']

# Secure: user-specific cache namespace
cache = caches[f'user_{request.user.id}_cache']

middleBrick also tests for template caching vulnerabilities by analyzing how user content is cached and whether proper escaping is applied before caching HTML content.

Integration with Django's middleware stack allows detection of cache poisoning through request processing. The scanner examines middleware ordering, as cache middleware placed before authentication can cause authenticated content to be cached and served to unauthenticated users.

Django-Specific Remediation

Securing Django against cache poisoning requires architectural changes and proper use of Django's caching features. Here are Django-specific remediation strategies:

1. Secure Cache Key Generation

from django.core.cache.backends.base import CacheKey
from django.utils.crypto import get_random_string

def generate_secure_cache_key(key_prefix, user_id, request_path):
    # Include user-specific salt to prevent key collisions
    user_salt = get_random_string(length=16)
    
    # Use consistent key format with namespace
    return f'secure_cache:{key_prefix}:{user_id}:{user_salt}:{request_path}'

@cache_page(3600, key_prefix='profile')
def user_profile(request, username):
    user = get_object_or_404(User, username=username)
    
    # Generate secure cache key
    cache_key = generate_secure_cache_key(
        'profile', 
        request.user.id if request.user.is_authenticated else 0,
        request.path
    )
    
    # Use Django's cache with secure key
    from django.core.cache import cache
    cached_data = cache.get(cache_key)
    
    if cached_data is None:
        data = {
            'username': user.username,
            'email': user.email,
            'bio': user.profile.bio
        }
        cache.set(cache_key, data, 3600)
    
    return JsonResponse(cached_data or data)

2. Proper Cache Invalidation

from django.db.models.signals import post_save
from django.dispatch import receiver

@receiver(post_save, sender=User)
def invalidate_user_cache(sender, instance, **kwargs):
    from django.core.cache import cache
    
    # Invalidate all cache entries for this user
    cache.delete_pattern(f'secure_cache:profile:{instance.id}:*')
    
    # Also invalidate related caches
    cache.delete_pattern(f'secure_cache:activity:{instance.id}:*')

3. Secure Template Caching

{% load cache %}

{# Secure template caching with user isolation #}
{% cache 600 user_content user.id request.user.id %}
    <div class="user-content">
        <p>{{ user.content|safe }}</p>
    </div>
{% endcache %}

4. Cache Middleware Configuration

# settings.py
MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.middleware.cache.FetchFromCacheMiddleware',  # After auth
    # ... other middleware
]

# Secure cache settings
CACHES = {
    'default': {
        'BACKEND': 'django.core.cache.backends.redis.RedisCache',
        'LOCATION': 'redis://127.0.0.1:6379/1',
        'KEY_PREFIX': 'secure_app',
        'TIMEOUT': 300,
        'OPTIONS': {
            'CLIENT_CLASS': 'django_redis.client.DefaultClient',
            'CONNECTION_POOL_KWARGS': {'max_connections': 50},
            'PARSER_CLASS': 'redis.connection.HiredisParser',
        },
    }
}

5. Cache Poisoning Detection Middleware

class CachePoisoningDetectionMiddleware:
    def __init__(self, get_response):
        self.get_response = get_response
        self.attack_patterns = [
            '../',  # Path traversal
            '//',   # Double slashes
            '%00',  # Null bytes
        ]
    
    def __call__(self, request):
        # Check for suspicious cache key patterns
        for pattern in self.attack_patterns:
            if pattern in request.path or pattern in request.GET.urlencode():
                from django.http import JsonResponse
                return JsonResponse({
                    'error': 'Suspicious cache key pattern detected',
                    'detail': f'Pattern: {pattern}'
                }, status=400)
        
        return self.get_response(request)

Frequently Asked Questions

How does Django's caching framework make it vulnerable to cache poisoning?

Django's caching framework can be vulnerable when user input directly influences cache keys or when cached content isn't properly segregated between users. The framework's flexibility in key generation and template caching can lead to scenarios where malicious users manipulate cache behavior to serve incorrect data to others.

Can middleBrick detect cache poisoning in Django applications?

Yes, middleBrick specifically scans for Django cache poisoning vulnerabilities by analyzing cache decorators, template tags, and middleware configurations. It tests for cache key collisions, improper user segregation, and template caching vulnerabilities. The scanner actively probes cache behavior to identify if user data can be leaked through cache manipulation.