MEDIUM timing attackfastapi

Timing Attack in Fastapi

How Timing Attack Manifests in FastAPI

A timing attack exploits measurable differences in how long an operation takes to infer secret data. In FastAPI applications the most common vector is the comparison of user‑supplied tokens, passwords, or API keys with stored values using Python’s default equality operator (==). The operator stops comparing as soon as it finds a mismatching character, so an attacker can guess each character by measuring response times.

Consider a login endpoint that manually checks a hashed password:

@app.post("/login")
async def login(form: OAuth2PasswordRequestForm = Depends()):
    user = await get_user_by_username(form.username)
    if not user or form.password != user.hashed_password:
        raise HTTPException(status_code=401, detail="Invalid credentials")
    return {"access_token": create_access_token(data={"sub": user.username})}

The != comparison leaks timing information about the password hash. Similar issues appear when validating JWT signatures, comparing API keys, or checking CSRF tokens where the code exits early on the first mismatch.

FastAPI’s dependency system can also introduce timing variations. For example, a dependency that queries a database for an API key and returns None on miss will cause a different code path (raising 401) versus a hit, and the database lookup time may vary with indexing or caching, giving an attacker another side‑channel.

FastAPI-Specific Detection

Detecting timing vulnerabilities in FastAPI starts with reviewing any secret comparison logic. Look for direct use of == or != on values that originate from user input and are compared to secrets such as passwords, hashes, API keys, or signing keys.

Static analysis tools can flag these patterns, but dynamic confirmation is essential because the timing difference may be subtle. middleBrick’s black‑box scanner includes timing‑attack checks as part of its Authentication and Input Validation categories. When you submit a URL, middleBrick sends a series of requests with gradually changing input (e.g., flipping one character at a time in a token) and measures response latency. Statistically significant variations trigger a finding with severity and remediation guidance.

You can run the same check locally with the middleBrick CLI:

middlebrick scan https://api.example.com/login

The output will include a section like:

Finding: Potential timing attack in password comparison
Severity: Medium
Category: Authentication
Remediation: Use constant‑time comparison (e.g., hmac.compare_digest).

Because middleBrick works unauthenticated and needs no agents, it can be integrated into CI pipelines (GitHub Action) to fail a build if a timing‑attack finding appears on a staging endpoint.

FastAPI-Specific Remediation

The fix is to replace non‑constant‑time equality with a constant‑time comparator. Python’s standard library provides hmac.compare_digest (or secrets.compare_digest for Python 3.3+), which runs in time independent of the input values.

Re‑write the login endpoint as follows:

import hmac
import hashlib

@app.post("/login")
async def login(form: OAuth2PasswordRequestForm = Depends()):
    user = await get_user_by_username(form.username)
    if not user:
        # Still use constant‑time compare to avoid user‑enum via timing
        hmac.compare_digest(b"dummy", b"dummy")
        raise HTTPException(status_code=401, detail="Invalid credentials")
    # Compare the hashed password (already a bytes object) with the supplied password hash
    if not hmac.compare_digest(user.hashed_password.encode(), hashlib.sha256(form.password.encode()).digest()):
        raise HTTPException(status_code=401, detail="Invalid credentials")
    return {"access_token": create_access_token(data={"sub": user.username})}

Note that we also hash the incoming password with the same algorithm used to store the hash before comparison; the hmac.compare_digest call ensures the comparison itself does not leak timing.

For API key validation, a similar pattern applies:

async def get_current_user(x_api_key: str = Header(...)):
    stored_key = await get_api_key_from_db(x_api_key[:8])  # lookup by key id
    if not stored_key or not hmac.compare_digest(stored_key, x_api_key):
        raise HTTPException(status_code=403, detail="Invalid API key")
    return user

When using an ORM, avoid early‑return patterns that depend on whether a record was found. Instead, fetch the record (or a dummy record) and then perform the constant‑time check.

Finally, leverage FastAPI’s built‑in security utilities where possible. fastapi.security.OAuth2PasswordBearer combined with passlib.context.CryptContext (which uses secrets.compare_digest internally) provides a vetted, constant‑time password verification path.

Frequently Asked Questions

Does middleBrick need any authentication to test for timing attacks on my FastAPI endpoint?
No. middleBrick performs unauthenticated, black‑box scanning; you only need to provide the public URL of the endpoint.
Can I use the same constant‑time comparison technique for JWT signature validation in FastAPI?
Yes. When verifying a JWT signature, replace any direct equality check with hmac.compare_digest on the computed signature and the signature extracted from the token.