Injection Flaws in Fastapi with Hmac Signatures
Injection Flaws in Fastapi with Hmac Signatures — how this specific combination creates or exposes the vulnerability
Injection flaws in FastAPI applications that use HMAC signatures can occur when signature verification is incomplete or applied to a subset of the request data, enabling attackers to bypass integrity checks. A common pattern is to sign only a subset of user-controlled inputs (e.g., selected query parameters or a JSON subset) while neglecting to include all parameters that ultimately reach business logic. If an endpoint adds extra parameters after signature verification or processes parameters that were not covered by the signature, an attacker can inject unexpected values that the server treats as safe.
For example, consider a FastAPI endpoint that verifies an HMAC over a JSON payload but does not canonicalize the input before signing. An attacker who can influence serialization behavior (such as field ordering or type coercion) might craft requests that produce different serialized bytes while appearing valid under the signature. Similarly, if the server reuses the signature for multiple downstream actions (e.g., using the same signed token for both read and write operations), an attacker may be able to replay or mutate parts of the request without invalidating the signature.
Another vector involves path or query parameters that are not included in the signed data but affect routing or parameter parsing. An attacker could inject additional path segments or query parameters that change how the framework interprets the request, potentially leading to insecure deserialization, command injection, or SQL injection when unchecked user input is later used. Even when HMACs protect the payload, missing integrity checks on headers, cookies, or URL components can allow injection through trusted channels.
In FastAPI, developers sometimes use dependency injection to validate and transform parameters before they reach route handlers. If these dependencies process untrusted data without re-verifying the signature context, they may inadvertently introduce injection opportunities. For instance, an attacker might supply specially crafted JSON that exploits type coercion rules or field name collisions to inject unexpected values that bypass intended validation logic.
These issues are especially relevant when the API design assumes that the HMAC alone guarantees integrity, without also ensuring complete input validation and strict schema enforcement. Attack patterns such as mass assignment, where an attacker supplies fields that map to sensitive model properties, can succeed if the server does not explicitly whitelist allowed fields after signature verification. This combination of HMAC-based integrity and unchecked injection surfaces creates a scenario where trust is placed in the signature while the application logic remains vulnerable to manipulation.
Hmac Signatures-Specific Remediation in Fastapi — concrete code fixes
To remediate injection risks when using HMAC signatures in FastAPI, ensure that the signature covers all inputs that influence server-side behavior and that validation occurs before any processing. Use strict schema validation and canonicalize data before signing. Below are concrete code examples demonstrating a secure approach.
Example: Signing and verifying the full request payload
Sign the entire JSON body after canonicalizing field order and types, and reject requests where the signature does not match the computed value.
import hashlib
import hmac
import json
from fastapi import FastAPI, Request, HTTPException, Depends
from pydantic import BaseModel, ValidationError
app = FastAPI()
SECRET_KEY = b"super-secret-key"
class Payload(BaseModel):
action: str
user_id: int
timestamp: int
def verify_hmac(request: Request) -> None:
body = request.body()
signature = request.headers.get("X-API-Signature")
if not signature:
raise HTTPException(status_code=400, detail="Missing signature")
computed = hmac.new(SECRET_KEY, body, hashlib.sha256).hexdigest()
if not hmac.compare_digest(computed, signature):
raise HTTPException(status_code=401, detail="Invalid signature")
@app.post("/action")
async def handle_action(request: Request, payload: Payload = Depends(verify_hmac)):
# At this point, signature covers the full canonical JSON body and payload is validated
return {"status": "ok", "action": payload.action}
Example: Canonical JSON for consistent HMAC computation
Ensure deterministic serialization to avoid signature mismatch due to formatting differences.
import json
def canonical_json(data: dict) -> bytes:
# Sort keys and use separators without whitespace for consistent bytes
return json.dumps(data, sort_keys=True, separators=(',', ':')).encode('utf-8')
payload_dict = {"action": "create", "user_id": 123, "timestamp": 1718000000}
signed_bytes = canonical_json(payload_dict)
signature = hmac.new(SECRET_KEY, signed_bytes, hashlib.sha256).hexdigest()
# Send signed_bytes as body and signature in X-API-Signature
Example: Including selected headers and path parameters in the signature
If business logic depends on headers or path parameters, include them in the signed material to prevent injection through those channels.
import hashlib
import hmac
from fastapi import Request
def compute_signature(secret: bytes, path: str, method: str, body: bytes, nonce: str) -> str:
message = f"{method}:{path}:{nonce}".encode() + body
return hmac.new(secret, message, hashlib.sha256).hexdigest()
async def verify_with_path_and_nonce(request: Request, path_param: str):
nonce = request.headers.get("X-Nonce")
if not nonce:
raise HTTPException(status_code=400, detail="Missing nonce")
signature = request.headers.get("X-Request-Signature")
body = await request.body()
expected = compute_signature(SECRET_KEY, request.url.path, request.method, body, nonce)
if not hmac.compare_digest(expected, signature):
raise HTTPException(status_code=401, detail="Invalid signature")
Best practices summary
- Sign all inputs that affect server-side logic, including body, selected headers, and path parameters.
- Use strict schema validation (e.g., Pydantic models) after signature verification to enforce types and prevent mass assignment.
- Canonicalize data before signing to avoid serialization-based bypasses.
- Use
hmac.compare_digestto prevent timing attacks during signature comparison. - Treat dependencies and validation layers as part of the trust boundary; do not assume earlier signature checks protect unchecked inputs.