HIGH rate limiting bypassdjangohmac signatures

Rate Limiting Bypass in Django with Hmac Signatures

Rate Limiting Bypass in Django with Hmac Signatures — how this specific combination creates or exposes the vulnerability

Rate limiting is commonly enforced by counting requests per identifier (IP, API key, or user). When HMAC signatures are used for request authentication, a mismatch between the rate-limiting identifier and the signed data can create a bypass. For example, if rate limiting is applied to the client IP but the HMAC is computed over a timestamp and a resource path without binding to the client identity, an attacker can rotate source IPs while keeping the signed payload valid. This decouples the signature verification from the rate-limiting context, allowing an attacker to generate many valid signed requests from different IPs without triggering limits.

In Django, this often occurs when the signature is computed over selected headers or query parameters but the rate limiter uses a different key, such as the raw IP address or an API key not included in the signed payload. Consider an endpoint that signs only the HTTP method and path, while rate limiting is based on an API key passed as a header that is excluded from the signature. An attacker who obtains a valid signature for one key could reuse it across multiple keys or IPs if the rate limiter does not correlate the signature with the limiting identifier.

Another common pitfall is weak nonce or timestamp handling. If the timestamp window used for replay protection is wide and the rate limiter does not track per-signature usage, an attacker can replay a signed request multiple times within the allowed window. Django’s cache or database rate limiters may not increment correctly if the same signed request is repeated from different IPs or with slightly altered nonces that still validate. This enables bypass through request distribution across sources or by manipulating nonces that the signature validation still accepts.

Additionally, if signature validation occurs after the rate limiter in the request lifecycle, an attacker can flood unsigned or unsigned-like requests to consume rate-limit capacity before valid signed requests are processed. In Django middleware order, placing the signature verification before rate limiting ensures that invalid or abusive traffic is rejected early, but if the limiter counts all incoming requests regardless of signature validity, the effective protection degrades. This ordering mismatch is detectable during combined spec and runtime analysis, where path parameters, headers included in the HMAC, and rate-limiting keys are cross-referenced.

These issues are not theoretical; they map to common weaknesses in API security testing, such as Broken Function Level Authorization (BFLA) and Improper Rate Limiting, which are included in checks like the 12 parallel security scans in middleBrick. Real-world patterns such as missing binding between the signed payload and the rate-limiting identifier can lead to abuse scenarios including credential exhaustion or unauthorized operations. By correlating the HMAC scope with the rate-limiting key and ensuring consistent identifiers across verification and limiting, you reduce the attack surface exposed by this combination.

Hmac Signatures-Specific Remediation in Django — concrete code fixes

To securely combine HMAC signatures and rate limiting in Django, ensure the identifier used for rate limiting is part of the signed payload. This binds the request origin to the signature and prevents decoupling attacks. Below is a complete, realistic example using hmac and hashlib with Django middleware and a rate limiter keyed by a combination of the API key and the signed components.

import hmac
import hashlib
import time
import django
from django.http import JsonResponse
from django.utils.decorators import method_decorator
from django.views import View

SECRET_KEY = b'your-secure-secret-key'  # stored safely, e.g., in env vars

def verify_hmac_signature(request):
    signature_header = request.META.get('HTTP_X_SIGNATURE')
    timestamp = request.META.get('HTTP_X_TIMESTAMP')
    api_key = request.META.get('HTTP_X_API_KEY')
    if not all([signature_header, timestamp, api_key]):
        return False
    # Ensure timestamp is recent (e.g., within 5 minutes) to prevent replay
    if abs(time.time() - int(timestamp)) > 300:
        return False
    payload = f'{api_key}:{timestamp}:{request.path}'.encode()
    expected = hmac.new(SECRET_KEY, payload, hashlib.sha256).hexdigest()
    return hmac.compare_digest(signature_header, expected)

@method_decorator(verify_hmac_signature, name='dispatch')
class ProtectedView(View):
    def get(self, request):
        api_key = request.META.get('HTTP_X_API_KEY')
        # Rate limit key includes API key and path to bind identity and resource
        rate_limit_key = f'ratelimit:{api_key}:{request.path}'
        # Implement rate limiting using Django cache (pseudocode)
        # from django.core.cache import caches
        # cache = caches['default']
        # if cache.get(rate_limit_key) > MAX_REQUESTS:
        #     return JsonResponse({'error': 'rate limit exceeded'}, status=429)
        # cache.incr(rate_limit_key)
        return JsonResponse({'status': 'ok'})

In this example, the HMAC is computed over the API key, timestamp, and request path. The rate-limiting key includes the API key and path, ensuring that each unique combination of client and endpoint has its own counter. This alignment prevents an attacker from reusing a signature across different API keys or IPs if the rate limiter uses a broader scope. The timestamp is validated server-side to mitigate replay attacks within an acceptable window.

Also enforce middleware ordering so that signature verification runs before counting the request toward the rate limit. In Django, place the HMAC verification middleware above any custom rate-limiting middleware in MIDDLEWARE settings. This ensures that unsigned or invalid requests are rejected early and are not counted against the rate limit, preserving the integrity of your limits.

For production, store SECRET_KEY in environment variables or a secrets manager, rotate keys periodically, and use HTTPS to prevent interception. Combine this approach with per-endpoint limits and monitoring to detect anomalies in request patterns. When integrated into your pipeline, tools like middleBrick can help validate that the signed fields and rate-limiting identifiers are correctly correlated during continuous scans.

Related CWEs: resourceConsumption

CWE IDNameSeverity
CWE-400Uncontrolled Resource Consumption HIGH
CWE-770Allocation of Resources Without Limits MEDIUM
CWE-799Improper Control of Interaction Frequency MEDIUM
CWE-835Infinite Loop HIGH
CWE-1050Excessive Platform Resource Consumption MEDIUM

Frequently Asked Questions

How can I ensure my HMAC includes the rate-limiting identifier?
Include the same identifier (e.g., API key) in both the HMAC payload and the rate-limiting key. For Django, compute HMAC over fields like API key, timestamp, and path, and use a combined string such as f'{api_key}:{timestamp}:{request.path}' for signing; then use the same api_key and path in your rate-limiting cache key.
What is the risk if timestamp validation is missing in HMAC-based rate limiting?
Without timestamp validation, an attacker can replay a valid signed request multiple times within the allowed window. Coupled with weak rate-limiting scopes, this can enable bypass by reusing signatures across IPs or keys, leading to excessive requests or abuse.