BOLA: Testing Object-Level Authorization in APIs

A hands-on methodology for finding broken object-level authorization across REST and GraphQL APIs, with a focus on authorization decisions rather than parameter guessing.

USER AAuthenticatedObject 1001AllowedObject 1002Test targetUSER AExpected: allowUSER B / TENANT BExpected: denyThe authorization decision must happen at the object boundary

PocForge technical illustration · conceptual attack path for educational testing.

Broken Object Level Authorization (BOLA) occurs when an API accepts a request for a specific object without correctly checking whether the caller is allowed to access that object. It is one of the most important authorization tests because the application may appear perfectly authenticated while still exposing another user’s data.

The core test is simple: change the object reference while keeping the authenticated identity the same. The hard part is doing this systematically across the application’s object graph.

Authorization is not authentication.An API can correctly identify User A and still incorrectly allow User A to read, modify or delete an object owned by User B.

Build an object map before attacking

Start by identifying object identifiers and relationships: user IDs, account IDs, order IDs, document IDs, project IDs, message IDs, invoice IDs, UUIDs and nested resource paths. Record which roles and tenants should be able to access each object.

USER AAuthenticatedObject 1001AllowedObject 1002Test targetUSER AExpected: allowUSER B / TENANT BExpected: denyThe authorization decision must happen at the object boundary

Illustrative BOLA test: User A is authorized for object 1001 but should be denied access to User B’s object 1002.

A repeatable test pattern

  1. Authenticate as User A.
  2. Perform a legitimate request for an object User A owns.
  3. Capture the request and identify the object reference.
  4. Replace the reference with an object belonging to User B or another tenant.
  5. Compare status code, response body, side effects and secondary API calls.
  6. Repeat with read, update and delete operations.

Do not stop at numeric IDs

UUIDs, opaque tokens and encoded identifiers can make enumeration harder, but they do not implement authorization. If you can obtain another valid identifier through a list endpoint, notification, browser history, mobile response or predictable workflow, the authorization check still matters.

REST and GraphQL variations

REST testing often focuses on path and body parameters such as /api/orders/1234 or {"orderId":1234}. GraphQL adds another layer: object references can be embedded inside queries, mutations, variables or nested resolver arguments. Test each resolver independently because a secure parent object does not guarantee secure child-object authorization.

Interactive: expected vs. vulnerable behavior

Change the requested object

Expected behavior: the server verifies that User A is authorized for object 1001 before returning or changing it.

Authorization test matrix

Operation Same user Other user Other tenant
GET Allow Deny unless explicitly shared Deny
PUT / PATCH Allow if role permits Deny Deny
DELETE Allow if role permits Deny Deny
Nested object Allow if parent + child authorized Deny Deny

What strong authorization looks like

Enforce authorization server-side at the point where the object is accessed. Prefer policy checks based on authenticated identity, tenant, role and object ownership. Avoid relying on hidden fields, client-side controls, object ID secrecy or UI restrictions.

For sensitive operations, log the authorization decision and test negative cases as part of automated integration testing. A regression suite should include cross-user and cross-tenant access attempts.

Further reading

OWASP’s API Security Project covers object-level authorization as a central API security concern. OWASP API Security Project.

← All researchDiscuss an assessment →