HIGH bola idorfastapihmac signatures

Bola Idor in Fastapi with Hmac Signatures

Bola Idor in Fastapi with Hmac Signatures — how this specific combination creates or exposes the vulnerability

Broken Object Level Authorization (BOLA) occurs when an API fails to enforce proper ownership checks between a subject (e.g., a user or service) and a targeted resource. In FastAPI, BOLA often manifests when endpoints use predictable identifiers (e.g., /users/{user_id}/profile) but only perform light authentication without verifying that the authenticated actor is permitted to access the specific resource identified by the ID.

Using HMAC signatures for request authentication can reduce certain risks, such as tampering with query parameters or headers, but it does not inherently prevent BOLA. A common pattern is to sign a subset of request metadata (e.g., user_id, timestamp, action) so the server can verify integrity and detect replay or manipulation. However, if authorization logic relies solely on the validity of the HMAC and omits a strict check that the authenticated principal matches the resource being requested, an attacker can forge or manipulate identifiers while keeping the signature valid.

Consider a FastAPI endpoint that deletes a payment record identified by payment_id. The client includes an HMAC-SHA256 signature computed over the concatenation of user_id, payment_id, timestamp, and a shared secret. If the server only validates the HMAC and then performs a delete based on payment_id without confirming that the user_id bound to the signature owns that payment_id, BOLA exists. An attacker who knows or guesses another payment_id can change the payment_id in the request while using their own user_id (or a static value) to produce a valid HMAC if the server does not bind the resource identifier to the principal in the authorization check.

In FastAPI, this often arises when route paths embed identifiers without scoping them to the authenticated subject. For example, a route like DELETE /payments/{payment_id} may validate an HMAC that includes the user_id but then operate on payment_id without verifying that the payment belongs to that user. Attack vectors include changing numeric IDs (IDOR), leveraging predictable UUIDs, or exploiting missing relationship checks between the subject and the resource. Even with HMAC integrity, the absence of per-request ownership verification means the API treats the request as authorized once the signature validates, effectively bypassing object-level controls.

Real-world attack patterns mirror classic OWASP API Top 10 A01:2023 broken object level authorization. For instance, an attacker can intercept or replay signed requests and alter identifiers if the server does not enforce strict ownership mapping. Similarly, enumeration risks arise when valid but unauthorized resources return different responses than non-existent ones, revealing existence without requiring proper access checks. Because HMAC schemes focus on integrity and authenticity of known parameters, they must be complemented by explicit authorization logic that ties each operation to the authenticated subject and the target resource.

Hmac Signatures-Specific Remediation in Fastapi — concrete code fixes

To remediate BOLA when using HMAC signatures in FastAPI, you must bind the authenticated principal to the resource and enforce ownership checks before any data access. The HMAC should cover enough request context to prevent tampering, while the authorization layer must validate that the subject identified by the signature has permission to operate on the target object.

Below is a complete, syntactically correct example that demonstrates secure handling. It uses HMAC-SHA256 over selected canonical components, verifies the signature, extracts subject and resource identifiers, and enforces ownership before proceeding. The approach avoids trusting the identifier alone and instead ties the operation to the authenticated principal.

from datetime import datetime, timezone
import hashlib
import hmac
import json
from typing import Optional
from fastapi import FastAPI, Depends, HTTPException, Header, status
from pydantic import BaseModel

app = FastAPI()

# In practice, store this securely (e.g., secrets manager) and rotate periodically)
SHARED_SECRET = b'example-secure-shared-secret-change-in-production'

# Minimal in-memory store for demonstration; replace with a proper database
payments_db = {
    1: {"user_id": "alice", "amount": 100},
    2: {"user_id": "bob", "amount": 200},
}

class PaymentRequest(BaseModel):
    payment_id: int
    user_id: str
    timestamp: str

class PaymentResponse(BaseModel):
    success: bool
    message: str

def verify_hmac(
    body: dict,
    signature: str,
    secret: bytes,
    tolerance_seconds: int = 300
) -> Optional[dict]:
    """
    Verify HMAC-SHA256 signature over a canonical JSON payload.
    Expects body keys: user_id, payment_id, timestamp.
    Rejects requests with stale timestamps to mitigate replay.
    """
    try:
        payload = PaymentRequest(**body)
    except Exception:
        return None

    # Check timestamp freshness
    try:
        req_time = datetime.fromisoformat(payload.timestamp.replace('Z', '+00:00'))
    except Exception:
        return None
    now = datetime.now(timezone.utc)
    if abs((now - req_time).total_seconds()) > tolerance_seconds:
        return None

    canonical = json.dumps({
        "user_id": payload.user_id,
        "payment_id": payload.payment_id,
        "timestamp": payload.timestamp
    }, separators=(',', ':'), sort_keys=True)
    expected = hmac.new(secret, canonical.encode('utf-8'), hashlib.sha256).hexdigest()
    if not hmac.compare_digest(expected, signature):
        return None

    return {"user_id": payload.user_id, "payment_id": payload.payment_id}

@app.delete("/payments/{payment_id}")
async def delete_payment(
    payment_id: int,
    x_signature: str = Header(..., alias="X-Signature"),
    x_user_id: str = Header(..., alias="X-User-Id"),
    x_timestamp: str = Header(..., alias="X-Timestamp"),
):
    # Build the body-like structure the HMAC was computed over
    body = {
        "user_id": x_user_id,
        "payment_id": payment_id,
        "timestamp": x_timestamp,
    }
    verified = verify_hmac(body, x_signature, SHARED_SECRET)
    if not verified:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="Invalid or expired signature"
        )

    # Critical: enforce BOLA — ensure the verified user owns this payment
    payment = payments_db.get(payment_id)
    if payment is None:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Payment not found")
    if payment["user_id"] != verified["user_id"]:
        raise HTTPException(
            status_code=status.HTTP_403_FORBIDDEN,
            detail="You do not have permission to access this resource"
        )

    # Perform the operation only after ownership is confirmed
    del payments_db[payment_id]
    return PaymentResponse(success=True, message="Payment deleted")

if __name__ == "__main__":
    import uvicorn
    uvicorn.run(app, host="127.0.0.1", port=8000)

Key points in this remediation:

  • The HMAC covers user_id, payment_id, and timestamp to bind the operation to a specific resource and moment.
  • Timestamp validation mitigates replay attacks; adjust tolerance according to your threat model.
  • Authorization explicitly compares the verified user_id from the signature with the resource’s owning user_id before any deletion.
  • Never treat the HMAC alone as proof of authorization; always enforce object-level checks scoped to the authenticated subject.

If you use an OpenAPI/Swagger spec, ensure the spec documents required headers and that the generator does not omit security scopes. The middleBrick CLI can scan endpoints to highlight where HMAC usage may coexist with missing ownership checks, helping you spot BOLA despite signature validation.

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 prevent BOLA in FastAPI APIs?
No. HMAC signatures help ensure request integrity and authenticity, but they do not replace object-level authorization checks. If your FastAPI endpoints do not verify that the authenticated subject owns the targeted resource, BOLA can still exist. Always couple HMAC validation with explicit ownership checks.
How can I test my FastAPI endpoints for BOLA when HMAC is used?
Use a scanner that supports runtime testing without credentials, such as the middleBrick CLI (middlebrick scan ), which runs unauthenticated checks including BOLA and can surface cases where signatures are present but ownership is not enforced. Combine automated scans with manual code review of authorization logic.