Source review: 13 September 2026. The procedures and tables below are proposed assessment practices. References support the technical behavior and source recommendations; they do not imply that a source mandates this exact scope or record format.
An API can authenticate every request correctly and still disclose or modify another customer’s data. Broken Object Level Authorization (BOLA), often called Insecure Direct Object Reference (IDOR), occurs when an application accepts an object identifier but does not verify that the caller may perform the requested action on that specific object. The identifier may be a number, UUID, filename, order reference, or indirect lookup key. Changing its format does not solve the authorization problem. 1
BOLA deserves early attention because APIs make object references easy to expose: URLs, request bodies, mobile clients, export jobs, and support tooling commonly carry them. The correct defensive response is not obscurity. It is an authorization decision made by the server for every object and every relevant action. 1
This guide describes an authorized assessment approach and a remediation model. Testing should use designated accounts and safe, representative records. It should not access real customer information beyond the approved scope or alter production data unnecessarily. 1
Model the resource and the decision
Before testing, document the API’s resources and actions. For each endpoint, identify the object it reads or changes, the identifiers it accepts, the user or service principal that calls it, and the allowed relationship between that principal and the object. The relationship might be ownership, organization membership, project membership, a delegated role, a case assignment, a support entitlement, or an explicit sharing grant. 1
Do not stop at standard create, read, update, and delete actions. Include download endpoints, exports, search filters, attachment retrieval, bulk operations, history, comments, webhooks, asynchronous jobs, administrative functions, and alternative API versions. A user might be blocked from reading an invoice directly yet obtain it through an export endpoint or a generated document link. 1
This model should distinguish tenant boundaries from role boundaries. A user may be entitled to records inside their organization but not records owned by another team. An administrator may manage account settings but not necessarily read all protected content. Treat “authenticated” as an identity state, not an authorization result. 1
Test object boundaries with controlled data
For a meaningful assessment, prepare at least two test principals with intentionally different relationships to representative objects: for example, two separate organizations, two projects under one organization, and a role with elevated but limited authority. Create or designate test records that are safe to read and modify. Record the expected outcome before sending a request. 1 3
Then verify that the API makes the same decision across routes and methods. A request should be denied when a caller changes an object reference to a record outside their permitted relationship. The service must apply that check to reads, writes, deletion, attachments, state transitions, and bulk operations. Verify both direct identifiers and identifiers nested in JSON, query parameters, pagination cursors, filters, or job definitions. 1 3
The test result needs careful wording. A successful response containing only the caller’s permitted data can confirm a control for that scenario, not prove the whole API is free of BOLA. A forbidden response may be correct, but its presence alone does not establish that all alternate routes are protected. Report the endpoint, object type, actor, intended relationship, response behavior, and the limit of the test. Avoid publishing real identifiers, tokens, request bodies, or customer data. 1 3
A fictional authorization decision table
The following matrix is a test-design artifact for a fictional invoicing API. “Northwind” and “Fabrikam” are synthetic organizations. It records an expected policy, not a claim about a real application. Assertions should verify the returned data and final object state as well as the HTTP result. 2
| Actor and scope | Action on synthetic object | Expected decision | Assertion |
|---|---|---|---|
| Northwind member | Read Northwind invoice | Permit | Only the requested Northwind invoice and allowed fields are returned |
| Northwind member | Read Fabrikam invoice | Deny | No Fabrikam invoice fields, attachment metadata, or existence detail are disclosed beyond the chosen error model |
| Northwind billing approver | Change the state of a Northwind invoice assigned to that role | Permit | State changes once, audit fields identify the approver, and unrelated fields remain unchanged |
| Northwind billing approver | Export Fabrikam invoices | Deny | No export job is created and no cross-organization data becomes available |
| Support role without an explicit grant | Download a Northwind invoice attachment | Deny | The attachment content and metadata remain unavailable |
The table covers object-level decisions only. A separate review is needed for function-level authorization, such as whether a role may use an administrative endpoint at all, and for property-level rules that hide or permit individual fields. 2
Common design failures
One design failure is authorization implemented in the client rather than the server. Hiding a button or filtering a mobile view does not control a direct API request. Another failure is an authorization check at the top-level resource but not on child resources such as files, comments, approval records, or report exports. 2
Teams also encounter time-of-check and time-of-use issues. A request may check membership when a job is created but execute later after access has been removed. Cache entries, signed download links, background workers, and event consumers need their own authorization and expiry decisions. The fact that an internal service generated the request does not automatically make the action safe. 2
Multi-tenant data access often fails when the tenant filter is optional, inferred from a client-provided field, or applied inconsistently between read paths. The tenant context should come from a trusted server-side identity or session claim and should be applied before object retrieval exposes data. Queries that retrieve an object globally and then compare ownership can also leak information through differences in error messages, timing, or metadata. 2
Build authorization into the application boundary
Use a server-side authorization layer that receives the authenticated principal, action, resource type, and resource identifier or scoped object. The layer should make a policy decision before the application returns content or changes state. Centralizing the decision reduces drift, but it must fit the application’s architecture; a small service may use a well-tested domain method while a platform may use a shared policy engine. 2
Scope data access at the query layer where possible. Instead of loading an object by identifier without context, retrieve it through a repository method that applies the caller’s tenant and relationship constraints. The service should still handle “not found” or “not permitted” consistently according to the product’s disclosure model. This reduces the chance that a new endpoint forgets an essential predicate. 2
Define policy in terms of verbs, not just roles. “Can read order,” “can approve payment,” and “can download attachment” are separate decisions. A role name is an input to the decision, not a universal permit. When access is delegated, track who granted it, what resource scope it covers, when it expires, and how revocation propagates to caches and background work. 2
Make testing durable
Unit tests should cover policy rules directly: permitted ownership, denied cross-tenant access, delegated access, expired access, and privileged exceptions. Integration tests should exercise actual endpoints with separate principals and records. Add tests for child resources, batch requests, asynchronous jobs, and alternate API versions where those patterns exist. 2
Test data must make failure visible. If both test organizations happen to have identical sample records, a response can look correct while being sourced from the wrong tenant. Use distinguishable, non-sensitive fixtures and assert both status and returned object scope. Do not depend on a front-end test alone; it cannot prove server-side enforcement. 2
For changes that affect authorization, require code review from someone able to assess the resource relationship. Security teams can provide patterns and coverage, but product owners often understand the business rule that distinguishes legitimate sharing from an exposure. Keep a versioned policy decision record for high-risk resources so maintainers can see why a rule exists. 2
Logging and response
Log authorization decisions at a level that supports investigation without creating a new sensitive-data store. Record the actor, action, resource type, result, request correlation identifier, and the policy reason or rule version where feasible. Limit raw payload logging and protect logs under the same data-classification rules as the application. 2 3
Alerting should focus on patterns such as repeated denied cross-scope requests, unusual volume across many object references, and administrative changes to sharing or policy configuration. A denial spike can also indicate a client defect, so triage needs product context. When a defect is confirmed, determine the affected endpoint versions, data classes, tenants, time window, and logs available for review before making claims about exposure. 2 3
Remediation sequence
First contain the affected route: disable the unsafe operation or enforce a conservative server-side scope while the fix is prepared. Then implement the object-level decision at every route that touches the resource, including background and export paths. Correct the data-access pattern so tenant and relationship constraints are mandatory, add regression tests with separate principals, and review nearby endpoints that share the same repository or serializer. 2
After deployment, verify with controlled records that permitted access still works and cross-scope access is denied. Review logs for signs of possible prior misuse of the authorization flaw only within the approved incident process. The durable fix is a coherent authorization model, not a blocklist of identifiers that happened to appear during testing. 2
FAQ
Are UUIDs enough to prevent IDOR?
No. A UUID may be hard to guess but can appear in links, logs, client traffic, exports, or referrals. The server must decide whether the caller may access the referenced object. 1
Should an API return 403 or 404 for an unauthorized object?
Choose a consistent behavior based on whether revealing an object’s existence is acceptable. Either response needs the same underlying authorization check and should be documented for clients. 2
Does an API gateway solve BOLA?
An API gateway can enforce broad authentication and routing rules. It usually does not know the application relationship between a caller and a particular invoice, file, or project. Object-level policy remains an application responsibility. 1
Internal links: API expertise · Broader API testing · JWT background · GraphQL differences
Want to learn more about this topic? Read my expertise page on Web Application Security →
Comments
No comments yet. Be the first!
Leave a Comment