Broken object-level authorization on invoices API
api.example.com · GET /v2/invoices/{id} · CVSS 9.1 · AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N
The invoices endpoint looks an invoice up by its numeric ID only. Any signed-in user can read any other tenant's invoices by changing the ID.
Steps to reproduce
- Sign in as a user of tenant B and copy the bearer token.
- Request an invoice ID that belongs to tenant A.
- The API returns 200 with the full invoice, including billing contact and line items.
Evidence
$ curl -H 'Authorization: Bearer <tenant_B>' \
https://api.example.com/v2/invoices/981
HTTP/1.1 200 OK
{"tenant":"A","contact":"billing@…","total":"18,400.00"}Impact
Every invoice on the platform is readable by any customer account. That exposes customer names, contacts and amounts, and is reportable under most customer contracts.
Recommended fix
Check the tenant on every object lookup in the service layer, not in the route, and add a regression test that requests another tenant's object.
Retest
Retested day 9: tenant B now gets 403 for tenant A's invoices.
Evidence mapping
- OWASP API Top 10
- API1:2023 Broken Object Level Authorization
- CWE
- CWE-639
- SOC 2
- CC6.1 logical access
- ISO 27001
- A.8.3 information access restriction