Timing Attack in Flask with Bearer Tokens
Timing Attack in Flask with Bearer Tokens — how this specific combination creates or exposes the vulnerability
A timing attack in Flask when Bearer tokens are used for authentication exploits differences in response time caused by early-exit logic in token validation. When a Flask route uses a Bearer token comparison that does not run in constant time, an attacker can infer information about the expected token by carefully measuring response times. For example, if the validation compares the submitted token character by character and returns immediately on a mismatch, the time taken reveals how many initial characters match. This is especially relevant for API endpoints that accept tokens via the Authorization header, where the server must extract the token and validate it before proceeding.
Consider a Flask route that manually checks a Bearer token string without using a constant-time comparison:
from flask import Flask, request, jsonify
import time
app = Flask(__name__)
# A hardcoded token for demonstration only
EXPECTED_TOKEN = 's3cr3t_t0k3n_abcdef123456'
@app.route('/api/data')
def get_data():
auth = request.headers.get('Authorization', '')
if not auth.startswith('Bearer '):
return jsonify({'error': 'missing bearer token'}), 401
token = auth.split(' ', 1)[1]
# Vulnerable: early exit on mismatch
if token != EXPECTED_TOKEN:
return jsonify({'error': 'invalid token'}), 401
return jsonify({'data': 'secret'})
if __name__ == '__main__':
app.run(debug=False)
In this example, an attacker can send tokens that progressively match the beginning of the expected token and observe that responses are faster when more characters are correct. Because the comparison token != EXPECTED_TOKEN fails early, the server returns sooner for incorrect prefixes. Over many requests, this leakage enables the attacker to recover the full token. Even if the token is long and random, timing differences caused by network jitter can be amplified by averaging multiple measurements.
Flask itself does not introduce the vulnerability; the issue arises from how the application validates the token. A common mistake is to compare tokens using Python’s default string equality, which is not constant-time. Additional risk occurs if the token is transmitted in URLs or logs, or if the endpoint’s response time varies based on other factors such as database queries, making it easier to distinguish a correct character from noise. The attack surface expands when the same token is used across multiple endpoints or when tokens are reused across services.
To understand the practical impact, consider how an attacker might probe the endpoint. They could send tokens of the same length as the expected token and measure round-trip time with high-resolution clocks. By observing that certain prefixes produce consistently faster responses, they can iteratively guess each character. This is a classic oracle-based timing attack. The presence of Bearer token authentication does not change the nature of the attack, but it focuses the attacker on the Authorization header as the key input to analyze.
In a real assessment, a scanner running 12 security checks in parallel would flag this as an Authentication finding if it detects timing-sensitive validation and can infer token structure through repeated probes. The scanner tests the unauthenticated attack surface, so it does not require credentials to observe timing differences. Because scans complete in 5–15 seconds, they can surface timing anomalies across endpoints that use Bearer tokens without additional instrumentation.
Bearer Tokens-Specific Remediation in Flask — concrete code fixes
Remediation focuses on ensuring token validation does not leak information through timing differences. The primary fix is to replace standard equality comparison with a constant-time comparison routine that always takes the same amount of time regardless of how many characters match. In Python, you can use hmac.compare_digest, which is designed for this purpose and is safe for comparing secret tokens.
Here is a corrected version of the earlier Flask route using constant-time comparison:
from flask import Flask, request, jsonify
import hmac
app = Flask(__name__)
EXPECTED_TOKEN = 's3cr3t_t0k3n_abcdef123456'
@app.route('/api/data')
def get_data():
auth = request.headers.get('Authorization', '')
if not auth.startswith('Bearer '):
return jsonify({'error': 'missing bearer token'}), 401
token = auth.split(' ', 1)[1]
# Secure: constant-time comparison
if not hmac.compare_digest(token, EXPECTED_TOKEN):
return jsonify({'error': 'invalid token'}), 401
return jsonify({'data': 'secret'})
if __name__ == '__main__':
app.run(debug=False)
hmac.compare_digest prevents early exit by iterating over the entire length of both strings, ensuring that response time does not depend on the number of matching characters. When using this approach, ensure that both arguments are of the same type (strings or bytes) and avoid mixing encodings. For byte-based tokens, compare token.encode('utf-8') with the expected bytes.
Additional remediation steps include avoiding timing variation from other parts of the request handling path. For example, ensure that the response path for valid and invalid tokens performs a similar amount of work, such as executing a dummy hash or a no-op database lookup if necessary to normalize timing. Also, transmit tokens only over HTTPS to prevent network-level leakage, and avoid including tokens in URLs where they may be logged in server access logs.
Developers who prefer explicit control can implement a constant-time loop, but using a well-audited standard library function is generally safer. The remediation should be verified by running the same timing tests that an attacker might use: measure response times for tokens that match increasingly longer prefixes and confirm that the distribution of response times does not reveal useful information. Tools and scripts that perform timing measurements can help validate that the fix removes the observable timing difference.
When managing many APIs, continuous monitoring helps catch regressions. The middleBrick CLI tool allows you to scan from terminal with middlebrick scan <url> to detect timing-related anomalies in your unauthenticated attack surface. Teams using CI/CD can integrate the GitHub Action to add API security checks to their pipelines and fail builds if risk scores degrade. For broader coverage across many services, the Pro plan supports continuous monitoring and alerts, while the MCP Server enables scanning APIs directly from AI coding assistants in the IDE.