Rate Limiting Bypass in Django with Bearer Tokens
Rate Limiting Bypass in Django with Bearer Tokens
Rate limiting in Django is commonly implemented using identifiers such as IP address or API key. When bearer tokens are used for authentication, relying solely on IP-based limits can create a bypass if the token is tied to a less granular or misconfigured limit. For example, if rate limiting is applied at the view or middleware level using only request.META.get('REMOTE_ADDR'), an authenticated request with a valid bearer token can be exempted from throttling because the IP check may be bypassed by proxies or load balancers that alter or strip headers. This mismatch allows an attacker to send many authenticated requests under a single compromised token while appearing to originate from a permitted IP, effectively bypassing intended rate controls.
In Django, this often occurs when throttling is configured using Django REST Framework’s (DRF) built-in throttling classes without accounting for authenticated user or token identity. A typical misconfiguration might look like a custom throttle class that checks IP but does not include the token or user as part of the scope. For instance, a developer might write:
from rest_framework.throttling import BaseThrottle
class IPRateThrottle(BaseThrottle):
def allow_request(self, request, view=None):
# Only using IP can be bypassed when bearer tokens are used
ip = request.META.get('REMOTE_ADDR')
# simplistic logic: no token/user consideration
return not self.is_exceeded(ip)
def is_exceeded(self, ip):
# pseudo-implementation
return False
When this throttle is used alongside token-based authentication, an attacker with a valid bearer token can generate high volumes of requests that share the same IP (e.g., through a proxy or NAT), while the throttle incorrectly allows the traffic because it does not factor the token into the rate-limiting key. This exposes authenticated endpoints to abuse, such as credential spraying, brute force, or scraping, even though the endpoint appears to be protected by a token. Additionally, if the token is leaked or stolen, the lack of token-aware rate limiting means the attacker can operate at scale before detection.
Another bypass scenario involves improperly ordered middleware or misconfigured proxy headers. If Django’s MIDDLEWARE processes after a security or rate-limiting middleware that relies on request.META['REMOTE_ADDR'], and a reverse proxy sets X-Forwarded-For without proper configuration, the effective remote address may be spoofed or lost. In tandem with bearer tokens, this can allow an attacker to manipulate perceived source IPs while retaining valid token credentials, further undermining rate limits. The interaction between token-based auth and IP-centric throttling creates a gap where authenticated requests are not uniformly tracked across token identity, leading to inconsistent enforcement.
To detect this class of issue during scans like those performed by middleBrick, security checks examine whether rate limiting incorporates authenticated context such as user ID or token hash in the throttle key. The scanner reviews throttle classes, authentication integration, and header-handling logic to identify gaps where token-authenticated requests are not properly constrained. These findings are reported with severity and remediation guidance, helping teams align rate limiting with actual authentication boundaries.
Bearer Tokens-Specific Remediation in Django
Remediation focuses on ensuring rate limiting considers the bearer token or authenticated user rather than only network-level identifiers. In Django REST Framework, use a throttle key that includes the token or user identifier. For token-based authentication, extract the token from the Authorization header and include it in the throttle scope. Example configuration:
from rest_framework.throttling import SimpleRateThrottle
class TokenUserRateThrottle(SimpleRateThrottle):
scope = 'token_user'
def get_cache_key(self, request, view=None):
# Use token or user for authenticated requests
if request.user and request.user.is_authenticated:
# Prefer token if present; fallback to user id
auth = request.auth
if auth:
return self.get_ident(request.user) + '|' + str(auth)
return self.get_ident(request.user)
return None
In settings, define the rate limit and map the throttle class:
REST_FRAMEWORK = {
'DEFAULT_THROTTLE_CLASSES': [
'myapp.throttles.TokenUserRateThrottle',
],
'DEFAULT_THROTTLE_RATES': {
'token_user': '100/minute',
}
}
This ensures each unique token–user pair is subject to its own rate window, preventing a single token from bypassing limits via IP sharing. For bearer tokens issued as opaque strings, include the token value (or a hash) in the throttle key to differentiate clients accurately:
import hashlib
def token_hash(token):
return hashlib.sha256(token.encode('utf-8')).hexdigest()
class TokenHashRateThrottle(SimpleRateThrottle):
scope = 'token_hash'
def get_cache_key(self, request, view=None):
auth = request.headers.get('Authorization', '')
if auth.startswith('Bearer '):
token = auth.split(' ', 1)[1]
return token_hash(token)
return None
Additionally, ensure proxy headers are handled securely so that spoofed X-Forwarded-For does not corrupt IP-based components of throttling. If you must incorporate IP, combine it with token/user rather than relying on it alone:
class CombinedRateThrottle(SimpleRateThrottle):
scope = 'combined'
def get_cache_key(self, request, view=None):
ip = request.META.get('REMOTE_ADDR', '')
auth = request.headers.get('Authorization', '')
if auth.startswith('Bearer '):
token = auth.split(' ', 1)[1]
return f'{ip}|{token_hash(token)}'
return ip
Configure middleware ordering so that authentication and throttle middleware operate after proxy headers are normalized. In production, validate that throttle scopes align with authentication mechanisms and that tokens are treated as primary identifiers for rate limiting. middleBrick scans can verify that throttle implementations incorporate token context and flag configurations that rely solely on IP when bearer tokens are in use.
Related CWEs: resourceConsumption
| CWE ID | Name | Severity |
|---|---|---|
| CWE-400 | Uncontrolled Resource Consumption | HIGH |
| CWE-770 | Allocation of Resources Without Limits | MEDIUM |
| CWE-799 | Improper Control of Interaction Frequency | MEDIUM |
| CWE-835 | Infinite Loop | HIGH |
| CWE-1050 | Excessive Platform Resource Consumption | MEDIUM |