HIGH insecure direct object referencefastapihmac signatures

Insecure Direct Object Reference in Fastapi with Hmac Signatures

Insecure Direct Object Reference in Fastapi with Hmac Signatures — how this specific combination creates or exposes the vulnerability

Insecure Direct Object Reference (BOLA/IDOR) occurs when an API exposes internal object references (e.g., database IDs, filenames) without ensuring the requesting subject has authorization to access that specific object. In FastAPI, this commonly arises when an endpoint uses user-supplied identifiers (such as item_id) to directly fetch a resource, relying only on authentication (e.g., an Hmac-signed token proving identity) without enforcing authorization checks that map the subject to the object.

Using Hmac signatures for authentication in FastAPI can give a false sense of security if the signature only validates the integrity and authenticity of the request (i.e., the payload and timestamp) but does not enforce per-subject access controls. For example, an endpoint that verifies an Hmac signature to ensure the request has not been tampered with might still directly load an object using an ID from the URL. Because the signature does not encode or enforce authorization boundaries, an attacker who knows or guesses another user’s object ID can change the ID in the request and access or modify resources that belong to other users.

Consider this FastAPI route that uses Hmac-based signature verification via a custom header:

from fastapi import FastAPI, Header, HTTPException, Depends
import hmac
import hashlib
import os
import time
from typing import Optional

app = FastAPI()

SECRET_KEY = os.environ.get("HMAC_SECRET", "change-me")

def verify_hmac_signature(
    timestamp: str,
    signature: str,
    body: str,
    secret: str = SECRET_KEY
) -> bool:
    """Verify that the HMAC-SHA256 signature matches the body and timestamp."""
    # Basic replay window (e.g., 5 minutes)
    if abs(time.time() - int(timestamp)) > 300:
        return False
    payload = f"{timestamp}.{body}".encode()
    expected = hmac.new(secret.encode(), payload, hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected, signature)

@app.get("/items/{item_id}")
async def read_item(
    item_id: int,
    x_signature: Optional[str] = Header(None),
    x_timestamp: Optional[str] = Header(None),
    x_body: Optional[str] = Header(None),
):
    if not (x_signature and x_timestamp and x_body):
        raise HTTPException(status_code=400, detail="Missing Hmac headers")
    if not verify_hmac_signature(x_timestamp, x_signature, x_body):
        raise HTTPException(status_code=401, detail="Invalid signature")
    # BOLA/IDOR risk: item_id is used directly without ownership check
    item = fetch_item_from_db(item_id)
    return item

In this example, the Hmac signature verifies that the request has not been altered, but it does not bind the item_id to the authenticated principal. An attacker who can obtain a valid Hmac-signed request for one item can simply modify the item_id in the URL and replay the request to access other items. The vulnerability is a BOLA/IDOR rooted in missing authorization checks, not in the Hmac algorithm itself, but in how the application uses authentication data to gate access to object references.

OWASP API Top 10 explicitly lists BOLA as a common API security risk, and this pattern demonstrates how authentication mechanisms like Hmac signatures must be complemented with proper authorization to avoid IDOR. Even when the signature prevents tampering, the API must enforce that a subject can only access resources they own or are permitted to access.

Hmac Signatures-Specific Remediation in Fastapi — concrete code fixes

To fix BOLA/IDOR when using Hmac signatures in FastAPI, you must ensure that every object access is validated against the subject derived from the signature or associated identity claim. The Hmac verification should either include a subject identifier (e.g., user ID) as part of the signed payload, or your authorization layer must enforce that the requesting subject owns the requested object.

Below is a concrete remediation pattern that binds the Hmac-signed identity to the resource access check. We include the user identifier inside the signed payload and enforce ownership in the route handler.

from fastapi import FastAPI, Header, HTTPException, Depends
import hmac
import hashlib
import os
import time
from typing import Optional

app = FastAPI()

SECRET_KEY = os.environ.get("HMAC_SECRET", "change-me")

def verify_hmac_signature(
    timestamp: str,
    signature: str,
    body: str,
    secret: str = SECRET_KEY
) -> bool:
    if abs(time.time() - int(timestamp)) > 300:
        return False
    payload = f"{timestamp}.{body}".encode()
    expected = hmac.new(secret.encode(), payload, hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected, signature)

def get_current_user_from_hmac(
    x_signature: str = Header(...),
    x_timestamp: str = Header(...),
    x_body: str = Header(...),
) -> dict:
    """
    Verify Hmac and extract user identity from the signed body.
    The body is expected to be a JSON string containing at least {"user_id": "..."}.
    """
    if not verify_hmac_signature(x_timestamp, x_signature, x_body):
        raise HTTPException(status_code=401, detail="Invalid signature")
    import json
    try:
        payload = json.loads(x_body)
    except json.JSONDecodeError:
        raise HTTPException(status_code=400, detail="Invalid body format")
    if "user_id" not in payload:
        raise HTTPException(status_code=400, detail="Missing user_id in payload")
    return {"user_id": payload["user_id"]}

@app.get("/items/{item_id}")
async def read_item(
    item_id: int,
    current_user: dict = Depends(get_current_user_from_hmac),
):
    # BOLA protection: ensure the item belongs to current_user
    item = fetch_item_from_db(item_id)
    if item is None:
        raise HTTPException(status_code=404, detail="Item not found")
    if str(item["user_id"]) != str(current_user["user_id"]):
        raise HTTPException(status_code=403, detail="Forbidden: you do not own this item")
    return item

Key points in this remediation:

  • The Hmac-signed body includes a user_id, so the signature binds the request to a specific identity.
  • get_current_user_from_hmac verifies the signature and extracts identity, raising 401 on failure.
  • The route compares the requested item_id against the ownership relationship in the database, ensuring that even with a valid Hmac signature, a user cannot access another user’s resources.

Additionally, you should apply consistent authorization checks across all endpoints and prefer parameterized queries or an ORM with strict access controls to avoid accidental exposure. This approach aligns with OWASP API Top 10 (2023) and common compliance mappings (e.g., SOC2, GDPR) that require clear access boundaries between subjects and objects.

Related CWEs: bolaAuthorization

CWE IDNameSeverity
CWE-250Execution with Unnecessary Privileges HIGH
CWE-639Insecure Direct Object Reference CRITICAL
CWE-732Incorrect Permission Assignment HIGH

Frequently Asked Questions

Does using Hmac signatures alone prevent IDOR in FastAPI?
No. Hmac signatures authenticate request integrity but do not enforce authorization. You must still validate that the authenticated subject is allowed to access the referenced object.
How does middleBrick handle BOLA/IDOR findings in FastAPI APIs with Hmac authentication?
middleBrick scans unauthenticated attack surfaces and reports BOLA/IDOR findings with severity and remediation guidance. It maps findings to frameworks like OWASP API Top 10 and provides actionable steps, such as enforcing ownership checks and binding identity into the signed payload.