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