Broken object-level authorisation in the invoice API
Description
Any authenticated user can retrieve an invoice belonging to another tenant by changing the numeric invoice identifier. The API verifies that the caller is authenticated and that the invoice exists, but does not verify that the invoice belongs to the caller’s tenant.
Evidence
GET /api/v1/invoices/1042 HTTP/1.1
Host: api.example.com
Authorization: Bearer <user-a-token>
HTTP/1.1 200 OK
Content-Type: application/json
{
"id": 1042,
"tenant": "tenant-b",
"customer": "Example Customer B",
"amount": 18400,
"status": "paid"
}Impact
An authenticated user from one tenant can read another tenant’s invoice metadata, including customer name, amount and status. In a multi-tenant product this is a cross-tenant data exposure and may create contractual and regulatory obligations.
Remediation
- Enforce object-level authorisation on every invoice read: derive the tenant from the authenticated principal and filter the query by both invoice ID and tenant ID.
- Add regression tests that request another tenant’s object with a valid session and expect 403 or 404.
- Do not rely on unguessable identifiers as the primary control. UUIDs reduce discoverability but do not replace authorisation.
References
- OWASP API Security Top 10 (API1:2023 Broken Object Level Authorization)
- CWE-639: Authorization Bypass Through User-Controlled Key
