HIGH injection flawsfastapihmac signatures

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_digest to 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.

Frequently Asked Questions

Can an attacker bypass HMAC verification by manipulating URL-encoded fields or form data in FastAPI?
Yes, if your HMAC does not cover URL-encoded or form data, an attacker can inject parameters that the server processes after verification. Always include all user-controlled data in the signed material and validate against a strict schema.
Is it safe to reuse the same HMAC for both read and write operations in FastAPI?
It is not recommended. Reusing signatures across different operations can enable replay or privilege escalation if an attacker can trick the server into applying a read-signature to a write action. Use distinct signatures or contextual nonces to limit scope.