Authorization at the Object Level: Testing for BOLA Before Someone Else Does
Broken object level authorization is consistently at the top of the OWASP API Security Top 10, and it is consistently missed by automated scanners. The reason is that it is not a code defect in the usual sense. The endpoint authenticates the caller correctly, returns a valid response, and the response contains data the caller should not have. Nothing is malformed. The request is simply for someone else's object.
A scanner that sends a request without a token gets a 401 and moves on. A scanner that sends a request with a valid token gets a 200 and moves on. The defect only appears when two identities are compared against the same object identifier.
The shape of the defect
An API endpoint that takes an object identifier and returns the object is the pattern to look for. The identifier is often sequential, sometimes a UUID, and occasionally a slug. The question is whether the handler verifies that the authenticated subject is permitted to access that specific object.
Three variants appear repeatedly:
- Missing check. The handler loads the object by identifier and returns it. Authorization was performed at the route level, not the object level.
- Check on the wrong field. The handler verifies that the object belongs to a tenant, but the tenant is taken from a request parameter rather than from the session.
-
Check that can be bypassed. The handler verifies ownership for the
GETpath but not forPATCH,PUTorDELETEon the same resource.
The third variant is common in APIs that were built incrementally, where the read path was reviewed and the write paths were added later.
Why automated scanning misses it
A scanner has no second identity. It can test for missing authentication, injection, and some classes of misconfiguration. Object-level authorization requires a comparison between what identity A can see and what identity B can see, and that comparison requires the scanner to hold credentials for both and to know which objects belong to which.
Some API security tools attempt this by creating two accounts and probing identifiers. That works for a subset of endpoints and produces false positives where the two accounts legitimately share data. The reliable method is still a test written by someone who knows the data model.
A test that finds real findings
A workable test per endpoint family:
- Identify the endpoints that take an object identifier. Path parameters, query parameters and body fields.
- Create two accounts in the same role, in different tenants or ownership scopes. Same role matters: a test between an admin and a user proves nothing about horizontal access.
- As account A, create an object and record its identifier.
- As account B, request that identifier on every method the endpoint supports. Read, update, delete, and any bulk or export variant.
- Record the response. A 403 is correct. A 200 with data is a finding. A 200 with an empty body is ambiguous and needs investigation.
- Repeat for identifiers that do not exist. A 404 for a non-existent object and a 403 for an existing object that is not yours is the correct pair. A 403 for both leaks existence; a 404 for both is usually acceptable.
The method coverage in step 4 is the part that is usually skipped. An endpoint that returns 403 on GET and 200 on PATCH is a finding.
The structural fix
Object-level authorization is easiest to get right when it is centralised. The pattern that holds up is a single authorization function that takes the subject and the object and returns a decision, called from every handler that loads an object by identifier. When the check is written inline in each handler, handlers added later will omit it.
Two supporting practices reduce the defect rate:
- Scope queries by the subject. Instead of loading an object by identifier and then checking ownership, include the subject in the query. A query that returns nothing for a foreign object cannot leak one.
- Test the write paths in the same suite as the read paths. The incremental addition of write methods is the most common source of the defect.
What to do with a finding
A BOLA finding is a data exposure finding, not a code quality finding. The response is to fix the handler, then to determine whether the endpoint has been used to access objects outside the caller's scope. That determination requires request logs with the subject, the object identifier and the response status. If those logs do not exist, the exposure window cannot be bounded.
That is the argument for logging the object identifier on authorization decisions, which is a small change with a large effect on the ability to answer the question later.
References
- OWASP, API Security Top 10 — API1:2023 Broken Object Level Authorization.
- OWASP, Authorization Cheat Sheet.
- OWASP, API Security Top 10 — API5:2023 Broken Function Level Authorization.
- NIST, SP 800-204: Security Strategies for Microservices-based Application Systems.
- IETF, RFC 6750: The OAuth 2.0 Authorization Framework: Bearer Token Usage.
Top comments (0)