Broken Object-Level Authorization in a Multi-Tenant API
An invoicing API checks that you are logged in, but not that the invoice is yours. One parameter change crosses tenants.
The lab API models a typical B2B SaaS: two organizations, an admin and a member in one of them, and invoices belonging to each. We tested every endpoint with each token, for every verb.
highXV-DEMO-08 · Verified by testInvoices readable across tenants by ID
Invoices readable across tenants by ID
Description
GET /api/invoices/:id loads by ID without checking the caller's organization.
Impact
Any authenticated user can read any organization's invoices.
Recommendation
Scope every lookup by the caller's organization, ideally in a shared data-access layer or with database row-level security.
highXV-DEMO-09 · Verified by testMembers can delete invoices via an unguarded verb
Members can delete invoices via an unguarded verb
Description
Role checks exist on PATCH but were never added to DELETE.
Impact
Least-privileged users can destroy financial records.
Recommendation
Evaluate one central policy function for every route and verb.
Measured in the lab: in vulnerable mode, a tenant-A member read tenant B's invoice (200) and deleted an invoice (204). In fixed mode, a single can(user, action, invoice) policy is applied to every verb, and all 12 cells of the authorization matrix return the intended status — including 404 rather than 403 for objects the caller may not see.
The lab code and tests for this case study live in labs/web2 of the Xevry website repository.
cd labs/web2npm installnode --test test/bola.test.jsFiles: labs/web2/src/invoices.js · labs/web2/test/bola.test.js
Let's discuss your security needs.
Tell us what you're building. We'll respond with clear questions, an honest view of whether we're the right fit, and a scoped proposal if we are.