HIGH timing attackflaskbasic auth

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 wrapper

Additional 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.

Frequently Asked Questions

Why does using == for password comparison in Flask create a timing vulnerability?
The == operator short-circuits on the first mismatching character, so an attacker can measure response times to learn how many initial characters are correct, gradually inferring the full value.
Does using Basic Auth over HTTPS fully protect against timing attacks?
HTTPS protects confidentiality in transit but does not prevent timing attacks on the server-side comparison logic; constant-time checks are still required to avoid information leakage.