Brute Force Attack in Flask with Bearer Tokens
Brute Force Attack in Flask with Bearer Tokens — how this specific combination creates or exposes the vulnerability
A brute force attack against a Flask API that uses Bearer tokens attempts to discover valid tokens by systematically trying many candidate values. Because Bearer tokens are typically passed in the Authorization header, the attack focuses on token validation logic rather than authenticating with a username and password. If token validation is performed without rate limiting, account lockout, or token introspection, an attacker can send many requests with different token values and infer validity based on response differences.
In Flask, common patterns that increase risk include custom decorators that check for a Bearer token in request.headers.get('Authorization') and then perform a lookup in a database or cache. If the lookup is slow or reveals whether a token exists through timing differences or distinct HTTP status codes (e.g., 401 vs 403), the endpoint becomes susceptible to enumeration. For example, an attacker might observe that a valid token returns 200 while an invalid token returns 401, enabling adaptive brute force or token guessing.
Without complementary controls such as rate limiting, the unauthenticated attack surface remains large because an attacker does not need credentials to target the token validation path. This is especially relevant when token generation is predictable or when leaked tokens are attempted at scale. The combination of Flask routes that accept Bearer tokens and missing protections like request throttling or token blacklisting means that brute force can be used to discover active sessions or infer token structure.
Additionally, if the API exposes verbose error messages or stack traces, attackers gain insight into token validation behavior, further aiding brute force campaigns. Findings from a scan often highlight missing rate limiting, weak token entropy, and inconsistent authorization responses as key risk factors. By correlating these findings with the API specification and runtime behavior, security teams can prioritize fixes that reduce the effectiveness of brute force attempts against Bearer token protected endpoints.
Bearer Tokens-Specific Remediation in Flask — concrete code fixes
To reduce brute force risk for Bearer tokens in Flask, apply rate limiting, enforce high-entropy tokens, and standardize authorization responses. Below are concrete code examples that demonstrate these protections.
1. Rate limiting with Flask-Limiter
Use a token bucket or fixed window to limit requests per token or per IP. This prevents rapid enumeration of valid tokens.
from flask import Flask, request, jsonify
from flask_limiter import Limiter
from flask_limiter.util import get_remote_address
app = Flask(__name__)
limiter = Limiter(app=app, key_func=get_remote_address)
@app.route('/api/resource')
@limiter.limit('10/minute') # limit requests per IP
def protected_resource():
auth = request.headers.get('Authorization', '')
if not auth.startswith('Bearer '):
return jsonify({'error': 'Unauthorized'}), 401
token = auth.split(' ')[1]
if not is_valid_token(token):
return jsonify({'error': 'Unauthorized'}), 401
return jsonify({'data': 'secure'})
def is_valid_token(token: str) -> bool:
# Replace with constant-time comparison and a secure lookup
return token in {'a3f8c...', 'valid_token_example'}
2. Constant-time token comparison
Avoid leaking timing information by using constant-time comparison to check token validity.
import hmac
import secrets
def is_valid_token(token: str) -> bool:
# Example: compare against a stored hash using hmac.compare_digest
stored_hash = 'expected_hash_here'
# In practice, store a hash and compare the hash of the provided token
return hmac.compare_digest(stored_hash, token) # simplistic example; use a hash in production
3. Standardized error responses
Return the same status code and message for invalid tokens to prevent enumeration via response differences.
@app.route('/api/endpoint')
def api_endpoint():
auth = request.headers.get('Authorization', '')
if not auth.startswith('Bearer '):
return jsonify({'error': 'Unauthorized'}), 401
token = auth.split(' ')[1]
if not is_valid_token(token):
# Always return 401 with a generic message
return jsonify({'error': 'Unauthorized'}), 401
return jsonify({'result': 'success'})
4. Secure token generation and storage
Generate tokens with sufficient entropy and store only hashes or use a secure token service.
import secrets
def generate_token() -> str:
return secrets.token_urlsafe(32) # 256-bit entropy