Imagine an API returning customer invoices. A logged-in user requests an invoice by number. What happens if they change it? If the server does not verify ownership, it returns another customer's data. This is an Insecure Direct Object Reference, or IDOR.
OWASP API Security Top 10 2023 lists this first as Broken Object Level Authorization (BOLA). It is widespread because servers often rely on identifiers supplied by clients.
A practical example
This is a fictional example. User A is logged into a customer portal:
- Original request: GET /api/v1/invoices/10452 with user A's token.
- Modified request: GET /api/v1/invoices/10453 with the same token.
Both return HTTP 200 and invoice data, but invoice 10453 belongs to another customer. The server checked authentication, not permission to access that record.
The same flaw can affect modification and deletion through PUT or DELETE. Identifiers can appear in parameters, request bodies, headers or download links, as well as URLs. In multi-tenant SaaS, the flaw may expose another organisation's data.
Why automated scanners often miss it
A scanner sees responses but lacks business rules. It cannot know invoice 10453 belongs to someone else. HTTP 200 and valid data appear correct. Unlike input handling flaws, IDOR uses entirely valid requests.
Other limitations include:
- Scanners often use one account, without a comparison.
- They do not know which identifiers are sensitive or related.
- Multi-step processes may reveal identifiers only later.
- Intentional sharing, such as shared documents, complicates interpretation.
Authorisation is therefore primarily tested manually, using at least two same-role accounts and ideally different organisations. Testers systematically attempt access to the other account's objects, following approaches such as the OWASP Web Security Testing Guide. Response comparison tools help, but findings need application context.
Function-level authorisation has a related flaw: ordinary users call administrative operations. OWASP lists this separately as Broken Function Level Authorization. Tests usually examine both levels.
Fixing IDOR reliably
The server must decide access on every request. Being logged in or not seeing another record's link is insufficient.
Useful principles include:
- Permission checks on every operation: reads, updates and deletes must verify user or organisation access.
- Identity from authenticated context: derive users and tenants from verified sessions or tokens where possible, rather than request parameters.
- Query-level scoping: find records by both identifier and owner or tenant, returning equivalent responses for missing and unauthorised records.
- Central authorisation: shared controls are harder to omit than scattered conditions.
- Deny by default: absent rules must not grant access.
- Automated tests: verify user B cannot access user A's data at each endpoint.
Permissions within organisations matter too. Access to a company does not imply access to every project or branch. Rules must reflect actual business processes.
Random identifiers such as UUIDs reduce guessing but do not fix authorisation: identifiers can appear in links, logs or shared email. OWASP recommends them only as an additional layer. See the Authorization Cheat Sheet and IDOR Prevention Cheat Sheet.
Developer checklist
- Every identifier-handling endpoint checks permissions server-side.
- User and tenant identity come from authenticated context.
- Database queries are scoped by owner or tenant.
- Authorisation is centralised and denies by default.
- Tests cover two users and two tenants.
- Files, exports, webhooks and background operations are included.
- Responses do not reveal whether another user's record exists.
We can verify roles and tenant separation through penetration testing or software security analysis. Arrange a consultation to define scope.