Timing Attack in Fastapi with Jwt Tokens
Timing Attack in Fastapi with Jwt Tokens — how this specific combination creates or exposes the vulnerability
A timing attack in a FastAPI application that uses JWT tokens typically arises during token validation, especially when comparing signatures or checking cryptographic checksums. In Python, the equality operator == used on strings performs a short-circuit comparison: it returns False as soon as a mismatching character is found. This behavior introduces variable execution time that depends on how many leading characters match. An attacker can measure response times across many requests and gradually infer the correct signature or secret, effectively leaking information through timing differences.
When FastAPI applications rely on libraries such as python-jose or PyJWT for decoding and verifying JWTs, the framework itself does not inherently introduce a vulnerability; the risk comes from how the application handles verification results. For example, manually inspecting claims after decoding and performing custom string comparisons—such as comparing a token identifier or a user-specific claim using ==—can expose the endpoint to timing-based inference. If the comparison logic is not constant-time, an attacker can send tokens with slightly altered values and observe small differences in response latency to deduce valid tokens or secrets.
Consider a FastAPI endpoint that retrieves a user profile using a JWT payload field such as sub to look up a database record. If the application first decodes the token, then performs an additional authorization check by comparing the sub claim to a list of allowed identifiers using a non-constant-time method, the timing variance can reveal whether a given identifier is valid. This is particularly dangerous in unauthenticated or public endpoints where an attacker can repeatedly probe the API without credentials. The attack does not break the cryptographic integrity of the JWT, but it can compromise specific business logic decisions or claim-based access rules that are not implemented with constant-time safeguards.
In practice, an attacker may send thousands of requests with systematically modified tokens, measuring response times to infer which claim values or signature bytes are correct. Because the scan methodology of middleBrick includes input validation and unsafe consumption checks, such logic flaws can be surfaced as findings. The tool does not interpret or fix the code, but it highlights risky patterns—such as non-constant-time comparisons in token handling—that can lead to information disclosure through timing channels.
To mitigate this class of issue, developers should rely on built-in, constant-time comparison utilities provided by cryptographic libraries and avoid writing custom equality checks for secrets or sensitive claims. In FastAPI, this means using the comparison functions offered by the underlying JWT library or the standard library when handling tokens, and ensuring that any authorization logic based on token content does not depend on branching that varies with secret data.
Jwt Tokens-Specific Remediation in Fastapi — concrete code fixes
Remediation centers on replacing non-constant-time operations with safe, library-provided functions and minimizing custom claim validation logic. Below are concrete, working examples for a FastAPI application that uses PyJWT for token verification.
Example 1: Safe JWT decoding and verification
Always use the library’s built-in verification and avoid post-decode string comparisons for secrets or signatures.
import jwt
from fastapi import FastAPI, Depends, HTTPException, status
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
app = FastAPI()
security = HTTPBearer()
SECRET_KEY = "your-secure-secret"
ALGORITHM = "HS256"
def decode_token(credentials: HTTPAuthorizationCredentials = Depends(security)):
try:
payload = jwt.decode(credentials.credentials, SECRET_KEY, algorithms=[ALGORITHM])
return payload
except jwt.ExpiredSignatureError:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Token expired",
headers={"WWW-Authenticate": "Bearer"},
)
except jwt.InvalidTokenError:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Invalid token",
headers={"WWW-Authenticate": "Bearer"},
)
@app.get("/profile")
def read_profile(payload: dict = Depends(decode_token)):
# Use payload data directly; do not perform manual string comparisons on secrets or identifiers
user_id = payload.get("sub")
if user_id is None:
raise HTTPException(status_code=400, detail="Missing subject claim")
return {"user_id": user_id, "data": "secure"}
Example 2: Constant-time claim checks (if required)
If you must compare claim values, use a constant-time comparison utility. Python’s hmac.compare_digest is appropriate for this purpose.
import jwt
import hmac
from fastapi import FastAPI, Depends, HTTPException, status
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
app = FastAPI()
security = HTTPBearer()
SECRET_KEY = "your-secure-secret"
ALGORITHM = "HS256"
EXPECTED_SUB = "user-123"
def decode_token(credentials: HTTPAuthorizationCredentials = Depends(security)):
try:
payload = jwt.decode(credentials.credentials, SECRET_KEY, algorithms=[ALGORITHM])
return payload
except jwt.InvalidTokenError:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Invalid token",
headers={"WWW-Authenticate": "Bearer"},
)
@app.get("/secure-action")
def perform_action(payload: dict = Depends(decode_token)):
user_id = payload.get("sub", "")
# Constant-time comparison to mitigate timing attacks
if not hmac.compare_digest(user_id, EXPECTED_SUB):
raise HTTPException(status_code=403, detail="Forbidden")
return {"status": "action executed"}
Best practices summary
- Prefer library-based verification: rely on
jwt.decodewithalgorithmsandoptionsto enforce validation rather than manual parsing. - Avoid branching on secret material: do not use
==to compare tokens, signatures, or sensitive claims. - Use
hmac.compare_digestfor any necessary equality checks on sensitive strings. - Keep token validation logic minimal and centralized, for example in a dependency function, to reduce the surface for accidental information leaks.
These code patterns help reduce the risk of timing-based inference while keeping token handling straightforward and aligned with common FastAPI practices.