Timing Attack in Flask with Cockroachdb
Timing Attack in Flask with Cockroachdb — how this specific combination creates or exposes the vulnerability
A timing attack in a Flask application using CockroachDB arises when response times reveal information about internal logic, such as whether a user exists or which character of a token matches. In Flask, developer patterns like early-return checks on database results can introduce measurable differences in request duration. CockroachDB, while PostgreSQL-wire compatible, has its own latency characteristics and query planning behavior; combined with network round trips and serialization, small timing differences can become observable at the HTTP layer.
Consider a login flow where Flask retrieves a user by username and then compares a password hash. If the code first fetches the record and then conditionally verifies the hash only when the record exists, an attacker can measure response times to infer username validity. With CockroachDB, queries may exhibit variable latency due to distributed consensus and replication factors, which can amplify timing differences compared to a single-node store. An attacker making numerous carefully timed requests can statistically distinguish faster responses (no matching row or early rejection) from slower ones (row found and hash computation proceeded).
Specific OWASP API Top 10 risks relevant here include excessive data exposure and improper error handling, because timing differences may be coupled with inconsistent messages or status codes. For example, returning a generic ‘Invalid credentials’ message is safer, but if the response is delayed only when valid credentials proceed to hash comparison, the timing divergence leaks information despite the generic message. Common patterns like using Python’s == for comparison instead of a constant-time function also worsen the issue, because short-circuit evaluation leaks position of the first mismatching byte.
In practice, an attacker might probe a Flask endpoint with crafted requests while measuring round-trip times. If usernames are looked up via CockroachDB with indexed queries, the presence of an index can make found-user paths consistently faster than missing-user paths, especially under light load. The attack surface expands if the API is unauthenticated or if authentication is rate-limited inconsistently. MiddleBrick’s checks for Authentication and Input Validation would flag inconsistent response behaviors, while its BOLA/IDOR and Rate Limiting checks help identify authorization and enumeration risks that often coexist with timing issues.
To illustrate a vulnerable pattern in Flask with CockroachDB using psycopg or an ORM like SQLAlchemy:
import time
from flask import Flask, request, jsonify
import psycopg
app = Flask(__name__)
def get_user_by_username(username):
conn = psycopg.connect(request.app.config["DATABASE_URL"])
cur = conn.cursor()
cur.execute("SELECT id, password_hash FROM users WHERE username = %s;", (username,))
row = cur.fetchone()
cur.close()
conn.close()
return row
@app.route("/login", methods=["POST"])
def login():
data = request.get_json()
username = data.get("username", "")
password_attempt = data.get("password", "")
user = get_user_by_username(username)
if user is None:
time.sleep(0.01) # naive attempt to mitigate timing, inconsistent
return jsonify({"error": "Invalid credentials"}), 401
# Simulated hash comparison (not constant-time)
if password_attempt == user[1]: # vulnerable to timing
return jsonify({"token": "abc123"}), 200
else:
return jsonify({"error": "Invalid credentials"}), 401
This example highlights timing risks: the if user is None branch introduces a measurable path difference, and the password comparison is not constant-time. An attacker can detect the username’s existence by observing response times across many requests.
Cockroachdb-Specific Remediation in Flask — concrete code fixes
Remediation focuses on ensuring that all code paths that interact with CockroachDB take approximately the same amount of time, regardless of whether a record exists. The primary technique is to replace early returns and conditional branches with constant-time workflows where feasible, and to use constant-time comparison functions for secrets.
First, avoid branching on the presence of a user before verification. Instead, always perform a constant-time comparison using a hash of a known value when the user is missing. This ensures that the server spends similar CPU and database time for valid and invalid inputs.
Second, enforce constant-time comparison for secrets. Python’s hmac.compare_digest is suitable for comparing password hashes or tokens. Also, prefer parameterized queries to avoid injection and ensure consistent query planning.
Third, consider masking timing variance at the network or application layer with artificial delays, but this is a weak mitigation compared to fixing the logic; prioritize constant-time logic patterns.
Below is a hardened Flask example with CockroachDB using psycopg:
import time
import hmac
from flask import Flask, request, jsonify
import psycopg
app = Flask(__name__)
def get_user_by_username(username):
conn = psycopg.connect(request.app.config["DATABASE_URL"])
cur = conn.cursor()
cur.execute("SELECT id, password_hash FROM users WHERE username = %s;", (username,))
row = cur.fetchone()
cur.close()
conn.close()
return row
@app.route("/login", methods=["POST"])
def login():
data = request.get_json()
username = data.get("username", "")
password_attempt = data.get("password", "")
user = get_user_by_username(username)
# Use a dummy hash to ensure constant-time behavior when user is absent
dummy_hash = "0" * 64 # adjust length to match your hash size
stored_hash = user[1] if user else dummy_hash
# Constant-time comparison
if not hmac.compare_digest(stored_hash, password_attempt):
# Optionally add minimal, consistent processing to mask timing
time.sleep(0.005)
return jsonify({"error": "Invalid credentials"}), 401
# Successful login path
return jsonify({"token": "abc123"}), 200
Key changes:
- No early return when user is missing; we substitute a dummy hash of equal length.
- Password comparison uses
hmac.compare_digest, which prevents timing leaks based on byte position. - Consistent control flow and minimal, predictable processing on both success and failure paths reduce timing distinguishability.
Additional recommendations include enforcing uniqueness constraints on usernames in CockroachDB to avoid duplicate rows that could affect query timing, and monitoring query latencies to detect anomalies. MiddleBrick’s LLM/AI Security and BOLA/IDOR checks can further validate that user-specific operations do not leak timing or authorization differences across endpoints.