Auth Bypass in Fastapi with Hmac Signatures
Auth Bypass in Fastapi with Hmac Signatures — how this specific combination creates or exposes the vulnerability
HMAC-based authentication in FastAPI can lead to an authentication bypass when the server-side verification logic is incomplete, inconsistent, or applied only to selected endpoints. A common pattern is to compute a signature on the client using a shared secret and a canonical representation of the request (method, path, selected headers, and body), then send the signature in a custom header such as X-API-Signature. The server is expected to recompute the signature using the same canonicalization and secret, and to reject the request if the signatures do not match.
Vulnerabilities arise when the server either skips verification for some routes, accepts multiple signature versions, or uses an unsafe comparison that is vulnerable to timing attacks. For example, if a FastAPI app applies signature verification only to POST /transfer but not to GET /account, an attacker can read sensitive data or perform actions without a valid signature. Incomplete canonicalization can also allow path or query parameter manipulation without changing the signature, enabling privilege escalation or unauthorized actions (BOLA/IDOR).
Another failure mode is treating the signature as a bearer token-like value without ensuring message integrity properties such as replay protection and timestamp validation. If the server accepts a static or weakly scoped signature, an attacker can reuse the signature across requests or sessions. Missing replay or nonce handling can turn the HMAC mechanism into a static credential that can be captured and reused, effectively bypassing authentication.
The risk is compounded when combined with other unchecked attack surfaces, such as excessive agency in LLM endpoints or unsafe consumption patterns that forward or transform requests. middleBrick detects these issues by correlating spec definitions containing security schemes with runtime behavior, highlighting cases where signature verification is missing, misconfigured, or applied inconsistently across the API surface.
Hmac Signatures-Specific Remediation in Fastapi — concrete code fixes
Remediation centers on consistent, server-side verification with a strict canonicalization method, constant-time comparison, replay/nonce controls, and applying the check to all authenticated endpoints. Below is a complete, realistic example using Python with FastAPI, the cryptography library, and standard libraries only.
import time
import hmac
import hashlib
from fastapi import FastAPI, Request, Header, HTTPException, status
from typing import Optional
app = FastAPI()
# Shared secret stored securely, e.g., from a vault or env var
SHARED_SECRET = b'example-strong-secret-change-in-production'
def canonicalize_request(method: str, path: str, timestamp: str, nonce: str, body: bytes) -> bytes:
# Canonical format: method|path|timestamp|nonce|body
parts = [method.upper().strip(), path.strip(), timestamp.strip(), nonce.strip(), body]
return b'|'.join(part if isinstance(part, bytes) else part.encode('utf-8') for part in parts)
def verify_signature(request: Request, received_signature: str) -> bool:
timestamp = request.headers.get('X-Timestamp')
nonce = request.headers.get('X-Nonce')
if not timestamp or not nonce:
return False
# Reject requests older than 5 minutes to mitigate replay
try:
req_time = int(timestamp)
except ValueError:
return False
if abs(time.time() - req_time) > 300:
return False
body = request.body()
canonical = canonicalize_request(request.method, request.url.path, timestamp, nonce, body)
expected = hmac.new(SHARED_SECRET, canonical, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, received_signature)
@app.middleware('http')
async def verify_hmac_signature(request: Request, call_next):
# Apply to all routes that require authentication; adjust paths as needed
if request.url.path.startswith('/api/'):
signature = request.headers.get('X-API-Signature')
if not signature or not verify_signature(request, signature):
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail='Invalid or missing signature'
)
response = await call_next(request)
return response
@app.post('/transfer')
async def transfer(request: Request):
return {'status': 'ok'}
@app.get('/account')
async def account(request: Request):
return {'account': 'data'}
Key remediation points embedded in the example:
- Consistent canonicalization across client and server using method, path, timestamp, nonce, and body with a deterministic separator.
- Timestamp and nonce checks to prevent replay attacks; reject requests older than a short window (e.g., 300 seconds).
- Use of hmac.compare_digest for constant-time signature comparison to avoid timing-based leakage.
- Applying the verification middleware to all authenticated routes, avoiding selective enforcement that leads to BOLA/IDOR or privilege escalation.
- Keeping the shared secret out of source code and loading it from a secure configuration or vault.
For more complex scenarios, include a versioned signature algorithm header (e.g., X-Signature-Algorithm) and reject unknown versions. Ensure that any body parsing (e.g., JSON) is performed before canonicalization and that the raw body is used to avoid mismatches caused by serialization differences.
middleBrick can validate these practices by checking spec-defined security schemes against runtime behavior, ensuring that required authentication is enforced uniformly and that findings are mapped to frameworks such as OWASP API Top 10 and PCI-DSS.
Related CWEs: authentication
| CWE ID | Name | Severity |
|---|---|---|
| CWE-287 | Improper Authentication | CRITICAL |
| CWE-306 | Missing Authentication for Critical Function | CRITICAL |
| CWE-307 | Brute Force | HIGH |
| CWE-308 | Single-Factor Authentication | MEDIUM |
| CWE-309 | Use of Password System for Primary Authentication | MEDIUM |
| CWE-347 | Improper Verification of Cryptographic Signature | HIGH |
| CWE-384 | Session Fixation | HIGH |
| CWE-521 | Weak Password Requirements | MEDIUM |
| CWE-613 | Insufficient Session Expiration | MEDIUM |
| CWE-640 | Weak Password Recovery | HIGH |