HIGH timing attackflask

Timing Attack in Flask

How Timing Attack Manifests in Flask

In a Flask application, timing attacks often appear when secret values (API keys, tokens, password hashes) are compared using non‑constant‑time operations such as the == operator or string .equals() methods. Because Python’s string comparison short‑circuits on the first mismatched character, an attacker can measure response times to guess each character of the secret.

A typical vulnerable pattern looks like this:

from flask import Flask, request, abort

app = Flask(__name__)
API_KEY = "s3cr3t_k3y_123"  # hard‑coded for illustration

@app.route("/data")
def get_data():
    provided = request.headers.get("X-API-Key")
    if provided == API_KEY:          # ← non‑constant‑time comparison
        return {"secret": "top‑secret"}
    abort(403)

if __name__ == "__main__":
    app.run()

An attacker sends a series of requests with varying prefixes of the API key and records the latency. Longer response times indicate a correct prefix, allowing the key to be reconstructed character by character. Similar issues arise in:

  • Custom token validation that uses == on JWT signatures.
  • Password verification that compares raw hashes instead of using werkzeug.security.check_password_hash (which internally uses constant‑time comparison).
  • Flask‑Login’s user.is_authenticated checks that inadvertently leak timing via database lookups.

These code paths are part of the unauthenticated attack surface that middleBrick probes during its 5‑15 second black‑box scan.

Flask-Specific Detection

middleBrick detects timing vulnerabilities by sending a large number of requests with carefully crafted input variations and measuring the response time distribution. Because the scanner works without agents or credentials, it treats the endpoint as a black box and looks for statistically significant timing differences that correlate with guessed characters of a secret.

When scanning a Flask API, you can use any of the middleBrick interfaces:

  • CLI: middlebrick scan https://api.example.com returns a JSON report that includes a finding under the "Authentication" category if a timing leak is detected.
  • GitHub Action: Add uses: middlebrick/action@v1 to your workflow; the action will fail the build if the security score drops below your threshold, catching regressions introduced by a new route that uses unsafe comparison.
  • MCP Server: From an AI coding assistant (Claude, Cursor, etc.), invoke the MCP server to scan the local Flask dev server directly from the IDE, getting immediate feedback before you push code.
  • Dashboard: After a scan, the dashboard shows a per‑category breakdown; the "Authentication" card will display a timing‑attack finding with severity, remediation guidance, and a trend line over time.

The finding includes the exact endpoint tested, the observed timing delta (e.g., "+12 ms per correct character"), and a proof‑of‑concept request pattern that demonstrated the leak.

Flask-Specific Remediation

Fixing timing attacks in Flask requires replacing any non‑constant‑time secret comparison with a constant‑time alternative. Python’s standard library provides hmac.compare_digest (or secrets.compare_digest in Python 3.3+), which runs in time independent of the input values.

Here is the corrected version of the vulnerable route:

from flask import Flask, request, abort
import hmac
import hashlib

app = Flask(__name__)
API_KEY = b"s3cr3t_k3y_123"  # keep as bytes for compare_digest

@app.route("/data")
def get_data():
    provided = request.headers.get("X-API-Key")
    if not provided:
        abort(401)
    # Constant‑time comparison
    if not hmac.compare_digest(provided.encode(), API_KEY):
        abort(403)
    return {"secret": "top‑secret"}

if __name__ == "__main__":
    app.run()

If you are validating JWTs or other tokens, use a library that already employs constant‑time comparison (e.g., PyJWT with jwt.decode and a verified secret). For password verification, rely on werkzeug.security.check_password_hash which internally uses secrets.compare_digest.

Additional Flask‑specific hardening steps:

  • Use Flask’s @app.before_request to enforce a minimum response time (e.g., time.sleep(0.005)) only as a defensive measure, not as a primary fix.
  • Enable Flask’s SECRET_KEY with sufficient entropy and never hard‑code it; load from environment variables or a secrets manager.
  • Regularly run middleBrick scans (via CLI, GitHub Action, or MCP Server) to ensure new routes do not reintroduce unsafe comparisons.

By adopting constant‑time comparison and leveraging middleBrick’s automated detection, you eliminate the timing side‑channel that attackers could otherwise exploit to steal secrets from your Flask API.

Frequently Asked Questions

Can middleBrick detect timing attacks in a Flask app that is behind a reverse proxy or load balancer?
Yes. middleBrick performs unauthenticated black‑box probing from the public Internet, so it measures the end‑to‑end response time that includes any proxy or load balancer. As long as the timing difference introduced by the vulnerable comparison is larger than the noise added by the infrastructure, the scanner will flag it.
Is it safe to rely on a fixed delay (e.g., time.sleep) to mitigate timing attacks in Flask?
Adding a constant sleep can reduce the signal‑to‑noise ratio, but it is not a reliable fix. An attacker can still collect enough samples to average out the delay, and the sleep unnecessarily impacts legitimate traffic. The recommended approach is to replace the secret comparison with a constant‑time function like hmac.compare_digest.