Denial Of Service in Flask with Bearer Tokens
Denial Of Service in Flask with Bearer Tokens — how this specific combination creates or exposes the vulnerability
Denial of Service (DoS) in a Flask API that uses Bearer tokens can arise when token validation is performed in a way that consumes disproportionate CPU or memory for certain crafted requests. A common pattern is to validate the token on every request before routing to the intended handler, often by calling functions that verify signatures, check revocation lists, or query an authorization service. If these checks are implemented inefficiently—for example, by performing expensive regular expression matching, repeated cryptographic operations, or unbounded iterations over large token lists—an attacker can send many requests that force these operations to run with specially crafted tokens, leading to high CPU usage or memory consumption.
Consider a Flask route that extracts a Bearer token from the Authorization header and validates it with a custom function:
from flask import Flask, request, jsonify
import jwt
app = Flask(__name__)
SECRET_KEY = 'super-secret-key'
@app.before_request
def verify_token():
auth = request.headers.get('Authorization')
if auth and auth.startswith('Bearer '):
token = auth.split(' ')[1]
try:
# Inefficient decoding or additional per-request checks can be costly
payload = jwt.decode(token, SECRET_KEY, algorithms=['HS256'])
# Additional custom checks (e.g., querying a database or large in-memory list)
# can further increase processing time per request
except jwt.ExpiredSignatureError:
return jsonify({'error': 'token expired'}), 401
except jwt.InvalidTokenError:
return jsonify({'error': 'invalid token'}), 401
else:
return jsonify({'error': 'missing token'}), 401
If the token validation logic includes operations with non-constant time complexity—such as iterating over a large set of revoked tokens for each request, or using regex patterns that exhibit catastrophic backtracking on maliciously crafted tokens—an attacker can send requests that cause the server to spend excessive CPU time per request. This is especially relevant when the validation logic interacts with external systems or large datasets without proper rate limiting or request cost controls. In a black-box scan, such inefficiencies can be detected as BFLA/Privilege Escalation or Rate Limiting findings when unbalanced cost per request enables resource exhaustion.
Moreover, if the API specification (OpenAPI/Swagger) defines Bearer token security schemes but does not enforce strict input constraints on token format, malformed tokens that trigger exceptions or deep recursion in parsing logic can amplify the impact. Because middleBrick scans the unauthenticated attack surface and runs checks in parallel, it can identify patterns where token validation paths correlate with high resource usage or inconsistent error timing, surfacing DoS risks alongside authentication and authorization concerns.
Bearer Tokens-Specific Remediation in Flask — concrete code fixes
To mitigate DoS risks when using Bearer tokens in Flask, focus on making token validation predictable in cost and resilient against malformed inputs. Use a well-maintained JWT library with constant-time verification where possible, avoid per-request linear scans, and enforce strict token format checks before performing expensive operations.
1. Validate token format early and reject malformed tokens with minimal processing:
import re
from flask import Flask, request, jsonify
app = Flask(__name__)
TOKEN_PATTERN = re.compile(r'^Bearer [A-Za-z0-9\-_=]+\.[A-Za-z0-9\-_=]+\.?[A-Za-z0-9\-_.+/=]*$')
@app.before_request
def verify_token():
auth = request.headers.get('Authorization', '')
if not TOKEN_PATTERN.match(auth):
return jsonify({'error': 'invalid authorization header format'}), 400
token = auth.split(' ', 1)[1]
# Proceed with efficient decoding
2. Use efficient decoding and avoid redundant checks on every request. Cache validation results when appropriate and keep revocation checks constant-time or indexed lookups:
import jwt
from flask import Flask, jsonify
app = Flask(__name__)
SECRET_KEY = 'super-secret-key'
# Assume a small, indexed revocation set for O(1) lookups
revoked_tokens = set()
@app.before_request
def verify_token():
auth = request.headers.get('Authorization', '')
if not auth.startswith('Bearer '):
return jsonify({'error': 'missing token'}), 401
token = auth.split(' ', 1)[1]
if token in revoked_tokens:
return jsonify({'error': 'token revoked'}), 401
try:
payload = jwt.decode(token, SECRET_KEY, algorithms=['HS256'])
# Attach payload for downstream use if needed
request.token_payload = payload
except jwt.ExpiredSignatureError:
return jsonify({'error': 'token expired'}), 401
except jwt.DecodeError:
return jsonify({'error': 'invalid token'}), 400
except Exception:
return jsonify({'error': 'invalid token'}), 400
3. Apply rate limiting at the API gateway or within Flask to limit the number of requests per token or client, reducing the impact of bursts designed to exhaust resources:
from flask import Flask, request, jsonify
from datetime import datetime, timedelta
app = Flask(__name__)
REQUEST_WINDOW = timedelta(seconds=60)
MAX_REQUESTS_PER_MINUTE = 30
# Simple in-memory store for illustration; use Redis or similar in production
request_log = {}
@app.before_request
def rate_limit():
client_id = request.headers.get('Authorization', 'anonymous')
now = datetime.utcnow()
window_start = now - REQUEST_WINDOW
request_log[client_id] = [t for t in request_log.get(client_id, []) if t >= window_start]
if len(request_log[client_id]) >= MAX_REQUESTS_PER_MINUTE:
return jsonify({'error': 'rate limit exceeded'}), 429
request_log[client_id].append(now)
These steps ensure that token validation remains efficient and that malformed or malicious tokens do not trigger disproportionate processing. By combining strict input validation, constant-time operations, and rate limiting, you reduce the attack surface for DoS while preserving the security benefits of Bearer token authentication.
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 |