HIGH broken access controlfastapihmac signatures

Broken Access Control in Fastapi with Hmac Signatures

Broken Access Control in Fastapi with Hmac Signatures — how this specific combination creates or exposes the vulnerability

Broken Access Control (BOLA/IDOR) in FastAPI when HMAC signatures are used incorrectly can allow an authenticated user to access or modify another user’s resources. A typical pattern is to include an HMAC-signed identifier (e.g., a record ID or a scoped token) in query parameters or headers, and to trust the signature without re-verifying ownership and authorization against the current user’s context.

Consider a FastAPI endpoint that accepts a signed_id query parameter. If the server only validates the HMAC signature and then directly uses the decoded identifier to fetch a database record, it may fail to confirm that the record belongs to the requesting user or that the user has the required permission. This is a BOLA/IDOR flaw: the signature proves integrity and optional tamper-evidence, but it does not substitute for access control checks. Attackers who can guess or enumerate IDs can simply reuse a valid signature (if the signing key does not bind the identifier to a user or tenant) or manipulate parameters outside the signature scope to reference other resources.

Another common pattern is signing a payload that includes user claims or a role claim. If the server trusts the signature but does not also validate that the subject or scope in the payload aligns with the authenticated session (for example, an API key or JWT subject), an attacker may swap roles (e.g., user to admin) within the payload while keeping the signature valid if the signer does not include the role inside the signed data or if the server reuses a key across tenants.

In FastAPI, this often manifests when HMAC verification is implemented as a dependency that returns a decoded payload, and the endpoint relies solely on that payload without performing additional authorization checks. For instance, a dependency might verify an Hmac-Signature header and attach user_id to the request state, but the endpoint then fetches an object by ID without ensuring that object.user_id matches the attached user_id. The signature may be correct, yet the access control boundary is missing, enabling horizontal privilege escalation across user-owned resources.

Real-world attack patterns resemble CVE-2021-28627-like scenarios where trust in a signed parameter leads to unauthorized access. The vulnerability is not in HMAC itself, which provides integrity, but in the authorization logic that omits resource ownership and scope validation. Identifiers signed with a shared secret can be replayed across users if the server does not embed tenant or user context in the signed data and enforce it at runtime.

To detect this with an unauthenticated scan, middleBrick runs checks that analyze OpenAPI specs and runtime behavior to see whether endpoints that accept signed identifiers also enforce per-request authorization. Findings are mapped to the BOLA/IDOR category in the security report and aligned with OWASP API Top 10 controls, ensuring you understand the access control gap regardless of whether you use the CLI (middlebrick scan ) or the Web Dashboard to track risk scores over time.

Hmac Signatures-Specific Remediation in Fastapi — concrete code fixes

Remediation centers on ensuring that HMAC-signed data includes all necessary authorization context (user_id, tenant, scope) and that the server re-validates ownership and permissions on every request. Below are concrete, working examples for FastAPI.

1. Include user_id and tenant in the signed payload and verify on each request:

import hmac
import hashlib
import time
import json
from fastapi import FastAPI, Depends, HTTPException, Request, Header, status
from pydantic import BaseModel

app = FastAPI()
SECRET = b'your-strong-secret'

def generate_signed_token(user_id: str, tenant_id: str, expires: int) -> str:
    payload = json.dumps({'user_id': user_id, 'tenant_id': tenant_id, 'exp': expires})
    signature = hmac.new(SECRET, payload.encode(), hashlib.sha256).hexdigest()
    return f'{payload}.{signature}'

def verify_hmac_signature(token: str) -> dict:
    if '.' not in token:
        raise HTTPException(status_code=401, detail='Invalid token format')
    payload, received_sig = token.rsplit('.', 1)
    expected_sig = hmac.new(SECRET, payload.encode(), hashlib.sha256).hexdigest()
    if not hmac.compare_digest(expected_sig, received_sig):
        raise HTTPException(status_code=401, detail='Invalid signature')
    data = json.loads(payload)
    if data['exp'] < time.time():
        raise HTTPException(status_code=401, detail='Token expired')
    return data

async def get_current_user(request: Request, authorization: str = Header(None)):
    if not authorization or not authorization.startswith('Bearer '):
        raise HTTPException(status_code=401, detail='Missing authorization')
    token = authorization[len('Bearer '):]
    return verify_hmac_signature(token)

@app.get('/records/{record_id}')
def get_record(record_id: str, user: dict = Depends(get_current_user)):
    # Critical: enforce ownership and tenant scope here
    # Example assumes you have a function fetch_record that returns a record dict
    record = fetch_record(record_id)
    if not record:
        raise HTTPException(status_code=404, detail='Record not found')
    if str(record['user_id']) != user['user_id'] or str(record['tenant_id']) != user['tenant_id']:
        raise HTTPException(status_code=403, detail='Access denied')
    return record

2. Use a dependency that binds the signed subject to the request and enforce authorization at the endpoint or with a custom dependency:

from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials

security_scheme = HTTPBearer()

def get_user_resource_dependency(requester: dict = Depends(get_current_user)):
    def dependency(resource_id: str):
        resource = fetch_record(resource_id)
        if not resource or str(resource['user_id']) != str(requester['user_id']):
            raise HTTPException(status_code=403, detail='Forbidden')
        return resource
    return dependency

@app.get('/records/{record_id}')
def get_record(
    record: dict = Depends(get_user_resource_dependency(requester=Depends(get_current_user)))
):
    return record

3. If using stateless HMAC tokens, embed a versioned schema and mandatory fields (user_id, tenant_id, permissions bitmask) and reject tokens that omit any required field. Combine this with rate limiting and short expirations to reduce replay impact.

These patterns ensure that HMAC signatures provide integrity while FastAPI enforces BOLA/IDOR checks, aligning with findings reported by middleBrick and remediations reflected in the Pro plan’s continuous monitoring and GitHub Action integrations.

Leverage the CLI (middlebrick scan <url>) or Web Dashboard to validate that your endpoints now require ownership checks and that findings no longer highlight BOLA/IDOR for these routes.

Frequently Asked Questions

Does a valid HMAC signature guarantee that the request is authorized?
No. A valid HMAC signature only proves the data has not been altered. Authorization requires additional checks that the resource belongs to the requesting user and that the user has the necessary permissions. Without these checks, BOLA/IDOR vulnerabilities exist.
How can I test if my FastAPI endpoints are vulnerable to Broken Access Control with HMAC signatures?
Use middleBrick to scan your API endpoints. The scanner checks whether endpoints that accept signed identifiers also enforce per-request ownership and scope validation. You can run the CLI (middlebrick scan ) or use the Web Dashboard to review BOLA/IDOR findings and see prioritized remediation guidance.