Timing Attack in Flask with Basic Auth
Timing Attack in Flask with Basic Auth — how this specific combination creates or exposes the vulnerability
A timing attack in Flask when using HTTP Basic Authentication occurs because the framework’s default credential comparison does not run in constant time. When you validate a username and password pair, Python’s == operator short-circuits: it returns False as soon as a character or token does not match. If a server performs a string comparison between the submitted password hash (or the decoded password) and the expected value, an attacker can measure response times and infer how many initial characters are correct. In the context of Basic Auth, the client sends an Authorization: Basic base64(username:password) header. If the server decodes this, extracts the password, and compares it naively to the stored value, subtle timing differences can reveal information about the password without triggering authentication failures in a noisy way.
Flask itself does not provide a built-in, constant-time comparison for credentials, so developers must implement it explicitly. Without such protections, even when passwords are hashed, timing discrepancies can emerge depending on how and where the comparison occurs—for example, comparing a hash before verifying a salt, or using non-constant-time checks on derived keys. Attackers can send many crafted requests and observe small differences in latency, gradually narrowing down the correct hash or key. Because Basic Auth transmits credentials on every request (albeit base64-encoded, not encrypted), any leakage about password validity or hash structure weakens the overall protection. This becomes especially critical in shared or noisier environments where an attacker can make repeated, low-volume measurements.
In a black-box scan, tools can send authenticated-style probes with slightly altered credentials and measure response times to detect whether the server behaves differently for valid versus invalid inputs. Findings may map to authentication weaknesses under frameworks such as OWASP API Top 10, where insufficient protection against brute-force or credential guessing is a common concern. For APIs analyzed by middleBrick, unauthenticated attack surface testing can surface timing-related anomalies when combined with known patterns of behavior, prompting developers to apply constant-time checks. The key takeaway is that Basic Auth in Flask requires deliberate, secure coding practices—primarily constant-time comparison—to prevent timing-based information disclosure across the network.
Basic Auth-Specific Remediation in Flask — concrete code fixes
To mitigate timing attacks in Flask with HTTP Basic Authentication, replace naive equality checks with a constant-time comparison function. Python’s hmac.compare_digest is the standard approach because it evaluates the entire string regardless of early mismatches. Always compare hashes or digests rather than raw passwords, and ensure the comparison is applied consistently wherever credential validation occurs.
Example of vulnerable code:
from flask import request, Response
import base64
VALID_USER = 'admin'
VALID_PASS = 's3cret' # In practice, store a salted hash
def login_required(f):
def wrapper():
auth = request.headers.get('Authorization')
if auth and auth.startswith('Basic '):
encoded = auth.split(' ')[1]
decoded = base64.b64decode(encoded).decode('utf-8')
username, password = decoded.split(':', 1)
if username == VALID_USER and password == VALID_PASS:
return f()
return Response('Unauthorized', 401, {'WWW-Authenticate': 'Basic'})
return wrapper
Example with remediation using hmac.compare_digest:
import base64 import hmac from flask import request, Response VALID_USER = 'admin' VALID_PASS_HASH = b'5e884898da28047151d0e56f8dc6292773603d0d6aabbdd62a11ef721d1542d8' # sha256 hash of 'password' def login_required(f): def wrapper(): auth = request.headers.get('Authorization') if auth and auth.startswith('Basic '): encoded = auth.split(' ')[1] decoded = base64.b64decode(encoded).decode('utf-8') username, password = decoded.split(':', 1) # Use constant-time comparison for the password hash if username == VALID_USER and hmac.compare_digest(VALID_PASS_HASH, password.encode('utf-8')): return f() return Response('Unauthorized', 401, {'WWW-Authenticate': 'Basic'}) return wrapperAdditional considerations: ensure the stored credential hash is salted and derived with a slow KDF (e.g., PBKDF2, bcrypt) to reduce offline brute-force risk. Even with constant-time comparison, weak passwords remain vulnerable to exhaustive guessing. In a production setup, prefer token-based or session-based authentication where feasible. middleBrick’s scans can help identify unauthenticated endpoints and detect timing-related anomalies; for deeper assurance, use the CLI (
middlebrick scan <url>) or the GitHub Action to enforce security gates in CI/CD pipelines before deploying changes.