Broken Object Level Authorization (BOLA) happens when an API accepts an object identifier from the caller and acts on that object without checking that this caller is allowed to touch it. It sits at number one in the OWASP API Security Top 10 2023 because it is easy to find, easy to exploit and present in almost every API that serves more than one user or tenant. This guide goes deeper than the overview in the Top 10 API Security Risks: it sets out what OWASP actually requires, how to build object-level checks into code and data access, how to test and evidence them, and how BOLA applies when the caller is a service account, integration or AI agent rather than a person. It is written for API developers, application security engineers, architects and testers.
Key takeaways
- BOLA is an object check, not an endpoint check. The caller is meant to reach the endpoint; the failure is that the code never asks whether this caller may act on this specific record.
- Comparing the token's user ID to the URL is not enough. OWASP states plainly that matching the session user ID against the ID parameter only fixes a small subset of cases; ownership, sharing, hierarchy and tenancy all need a policy.
- Scope the query, do not filter the result. Look objects up inside the set the caller may access (for example the caller's tenant and relationships), and return the same 404 for "missing" and "not yours".
- Random identifiers are defence in depth only. UUIDs slow enumeration, but identifiers leak through logs, links and other endpoints; the authorisation check is the control.
- Machine callers need object scoping too. An API key or service token that can read every tenant's objects turns one leaked credential into a full data breach, so non-human identities must carry tenant and object scope.
What it is and who it applies to
OWASP defines object level authorisation as an access control mechanism, usually implemented in code, that validates a user can only access the objects they should have permission to access. Its rule is simple: every API endpoint that receives an ID of an object and performs any action on it should implement an object-level check that the logged-in subject may perform the requested action on the requested object.
The identifier can be a sequential integer, a UUID or a generic string such as a shop name or vehicle identification number (VIN). It can arrive in the path, the query string, a header or the request body, including GraphQL variables and batch arrays. OWASP rates the risk:
| OWASP factor | API1:2023 rating | What it means in practice |
|---|---|---|
| Exploitability | Easy | Changing an ID in a request needs no special tooling; the response shows whether it worked |
| Prevalence | Widespread | Servers rely on client-supplied IDs rather than tracking client state, so the pattern is everywhere |
| Detectability | Easy | Two test accounts and a proxy find most cases |
| Technical impact | Moderate | Disclosure, modification or deletion of other users' data, and in some cases full account takeover |
BOLA versus BFLA: OWASP draws the line clearly. If the caller is entitled to the endpoint but manipulates the ID to reach someone else's object, that is BOLA (API1). If the caller reaches a function it should not use at all, such as an admin route, that is Broken Function Level Authorization. See the OWASP API Top 10 API5 Broken Function Level Authorization Guide. If the caller may read the object but sees or sets fields it should not, that is property-level, covered in the OWASP API Top 10 API3 Broken Object Property Level Authorization Guide.
Who it applies to: any organisation that publishes REST, GraphQL, gRPC or mobile back-end APIs with more than one user, customer or tenant. The underlying weakness is CWE-639 (Authorization Bypass Through User-Controlled Key), which MITRE also lists under the alternate terms IDOR and horizontal authorisation, with CWE-285 (Improper Authorization) as the parent concept OWASP cites.
Structure and key requirements
The OWASP entry has four prevention requirements. Read together with the OWASP Authorization and IDOR Prevention cheat sheets and ASVS 5.0, they translate into the following engineering requirements.
| OWASP requirement | Engineering interpretation | ASVS 5.0 anchor |
|---|---|---|
| Implement an authorisation mechanism that relies on user policies and hierarchy | Document who may do what to which objects: owner, shared-with, team, organisation, tenant, delegated admin. Prefer attribute or relationship based rules over role-only checks | V8.1.1 (documentation of function and data-specific rules) |
| Check access in every function that uses client input to access a record | One enforcement point per data access path, applied to read, update, delete, export, bulk and nested resources, at a trusted server layer | V8.2.2 (data-specific access, L1), V8.3.1 (trusted service layer) |
| Prefer random, unpredictable GUIDs for record IDs | Expose UUIDv4 or long random values publicly; keep sequential keys internal. Treat as hardening only | No direct requirement; supports V8.2.2 |
| Write tests for the authorisation mechanism and block deployment if they fail | Cross-user and cross-tenant tests in CI, generated from an authorisation matrix | V8.2.2 verified by test evidence |
Two further ASVS 5.0 requirements matter for APIs: V8.4.1 (L2) requires cross-tenant controls so operations never affect tenants the consumer has no permission for, and V8.3.3 (L3) requires that access to an object is based on the originating subject's permissions, not those of an intermediary service. The second one is where many microservice architectures fail, and it leads directly into the identity section below.
What a correct check looks like
- Subject from the credential: take the caller's identity, tenant and roles from the validated token or session on the server, never from a header, cookie value or body field the client controls.
- Scoped lookup: the OWASP IDOR cheat sheet contrasts a global lookup such as
Project.find(params[:id])with a scoped one such ascurrent_user.projects.find(params[:id]). Apply the same idea in SQL (a mandatorytenant_idpredicate, or database row-level security) and in ORM default scopes. - Action-aware decision: read access does not imply update or delete. The decision takes subject, action and resource, as a policy engine call would.
- Uniform denial: return the same status (usually 404) for an object that does not exist and one the caller may not see, so the API is not an existence oracle.
- Every path: nested routes (
/orgs/{orgId}/invoices/{invoiceId}must check that the invoice belongs to that org and the caller to the org), batch endpoints that take arrays of IDs, file and export endpoints, websocket subscriptions, and GraphQLnodeornodesfields that resolve any object by global ID.
OWASP's three scenarios show the range: a shop analytics endpoint at /shops/{shopName}/revenue_data.json enumerated using a list of shop names from another endpoint; a connected-car API that accepts any VIN to start engines and unlock doors; and a GraphQL deleteReports mutation that deletes whichever document keys it is given. None needs a vulnerability in the usual sense, only a missing check.
Identity and NHI requirements
BOLA is usually explained with two human users, but the riskiest callers of many APIs are non-human: partner integrations, mobile back ends, batch jobs, internal microservices and, increasingly, AI agents calling tools. Object-level authorisation has to work for them as well.
| Caller type | Typical BOLA failure | Control |
|---|---|---|
| Customer API key or OAuth client | Key authenticates the customer but the API never binds it to that customer's tenant, so changing an account ID returns another customer's data | Bind every credential to a tenant and check that binding in the data layer. See the API Key Management Guide. |
| Internal microservice | Downstream service trusts any call from the upstream service account and returns any object it asks for | Propagate the end user's identity (ASVS V8.3.3) using token exchange, and authorise on the originating subject. See the OAuth Token Exchange and Delegation Guide. |
| Admin or support tooling | Support account can read any record by ID with no ticket or reason, and its credential is weakly protected | Scope support access by case, log reason codes, require strong authentication |
| AI agent using API tools | Agent runs with a broad service credential; a prompt or a hallucinated ID makes it fetch another user's object, and the API sees an authorised caller | Act on behalf of the user with delegated, scoped tokens and enforce object checks in the API, not in the prompt. See the AI Agent Authorisation Guide. |
- Least privilege for NHIs is object scope, not just API scope: an OAuth scope such as
invoices:readsays which function is allowed; it says nothing about whose invoices. Tenant and resource constraints must come from the credential's binding or from claims such as an audience or tenant ID checked server side. See the OWASP NHI Top 10 NHI5 Overprivileged NHI Guide. - Do not trust identifiers in tokens you did not validate: a JWT claim is only as good as its signature validation, audience check and issuer check. See the JWT Security Guide.
- Credential leaks multiply BOLA: a leaked partner key for an API without object checks exposes every customer, not one. Pair the object check with secret hygiene and rotation.
Implementation steps
- Inventory object-bearing operations. From the OpenAPI or GraphQL schema, list every operation that takes an object identifier in path, query, header or body, including arrays and nested IDs. Unknown or shadow endpoints are where checks are missing, so reconcile against gateway traffic.
- Write the authorisation model. For each resource type, document the relationships that grant each action (owner, member of project, tenant admin, delegated support). This satisfies ASVS V8.1.1 and gives testers the expected answers. See the Authorisation Models Guide.
- Choose one enforcement pattern. Options are a shared authorisation library called from data access code, a policy decision point (OPA, Cedar or a relationship based service) called with subject, action and resource, or database row-level security for tenancy. Avoid leaving each handler to write its own
ifstatement. See the Policy-as-Code for Authorisation Guide. - Make the scoped lookup the default. Repository methods take the caller context as a required argument; unscoped lookups are only available to clearly named internal functions and are flagged in code review. Deny by default when no rule matches.
- Replace public sequential IDs. Issue random public identifiers for new objects and migrate high-risk resources first. Do not encrypt IDs as a substitute; the OWASP cheat sheet advises against it because it is hard to do securely.
- Add authorisation tests to CI. Build an authorisation matrix of roles, tenants, operations and expected results, and run every object operation as an owner, a peer in the same tenant, a user in another tenant and an unauthenticated caller. Fail the build on any mismatch, as OWASP requires.
- Roll out safely. Run a new central policy in log-only mode first, compare its decisions with production behaviour, fix false denials, then enforce per service. Keep the previous enforcement path behind a feature flag so a faulty policy can be rolled back in minutes rather than by redeploying.
- Instrument decisions. Log each denial with subject, client ID, tenant, resource type and object ID (not the object content) so detection rules can spot enumeration. See the Secure Error Handling and Logging Guide.
Choosing an enforcement approach
| Approach | Best fit | Trade-offs |
|---|---|---|
| Scoped queries in the application (ORM scopes, repository pattern) | Small teams, a monolith, simple ownership | Cheap and fast; consistency depends on discipline and code review |
Database row-level security on tenant_id | Multi-tenant SaaS with shared tables | Strong tenant isolation even if a handler forgets; does not express in-tenant sharing well |
| Central policy engine (ABAC or ReBAC) | Many services, complex sharing, audit requirements | Consistent and testable; adds latency and an availability dependency, needs caching and a fail-closed decision |
| Gateway-only checks | Coarse tenant routing | Gateways rarely know object ownership; use as an extra layer, never the only one. See the API Gateway Security Guide. |
Smaller organisations usually get most of the benefit from scoped queries plus row-level security and a cross-tenant test suite. Larger organisations with dozens of services benefit from a shared decision service because it makes the rules testable and auditable in one place. For multi-tenant platforms, see the Multi-Tenant SaaS Architecture Security Guide.
Evidence and assessment
Assessors and auditors will want proof that object checks exist everywhere and keep working after each release.
- Design evidence: the documented authorisation model per resource and the authorisation matrix (ASVS V8.1.1).
- Test evidence: CI results from cross-user and cross-tenant tests, with coverage measured against the inventory of object-bearing operations. The OWASP Authorization Testing Automation cheat sheet describes a matrix file listing roles, services and the expected allowed (200) or denied (403) codes, driving one integration test per role.
- Manual testing: authorised penetration tests with at least two accounts per role and tenant, covering GraphQL, batch and export paths. See the API Penetration Testing Guide.
- Code review evidence: static analysis or review rules that flag unscoped lookups, raw
findByIdstyle calls in handlers and resolvers without an authorisation call.
Detection signals and metrics
| Signal or metric | Why it matters | Source |
|---|---|---|
| Distinct object IDs requested per identity per hour | Enumeration shows as one caller touching far more objects than normal | Gateway or application logs |
| Ratio of 403/404 responses per identity on object endpoints | Guessing produces bursts of denials, especially with sequential IDs | Gateway logs, WAF |
| Cross-tenant access attempts (subject tenant differs from object tenant) | Should be zero outside named support paths | Authorisation decision logs |
| Percentage of object-bearing operations covered by automated authorisation tests | Shows whether the control is evidenced; target 100% for internet-facing APIs | CI reports against the API inventory |
| Mean time to fix BOLA findings | Easy-to-exploit issues need short remediation targets | Vulnerability management system |
| Share of public resources using random identifiers | Tracks hardening of enumeration-prone resources | Schema review |
Mapping to other frameworks
| Framework | Reference | Relevance to API1 |
|---|---|---|
| OWASP ASVS 5.0 | V8.1.1, V8.2.2, V8.3.1, V8.3.3, V8.4.1 | Documentation, data-specific access, server-side enforcement, originating subject, cross-tenant isolation |
| MITRE CWE | CWE-639, CWE-285 | Authorization bypass through user-controlled key; improper authorization |
| MITRE CAPEC | CAPEC-1, CAPEC-122 | Accessing functionality not properly constrained by ACLs; privilege abuse |
| NIST SP 800-53 Rev 5 | AC-3, AC-6, SA-11, AU-12 | Access enforcement, least privilege, developer testing, audit record generation |
| NIST CSF 2.0 | PR.AA-05, PR.PS-06, DE.CM-09 | Permissions enforced with least privilege; secure development; monitoring of software and data |
| MITRE ATT&CK v20 | T1190, T1213 | Exploit Public-Facing Application; Data from Information Repositories |
| ISO/IEC 27001:2022 Annex A | 5.15, 8.3, 8.26, 8.28 | Access control, information access restriction, application security requirements, secure coding |
| CIS Controls v8.1 | Control 6, Control 16 | Access control management; application software security |
For a wider view of where the API Top 10 sits against other OWASP projects, see the OWASP ASVS Guide and the OWASP Top 10 Guide, where broken access control is the web equivalent.
Common pitfalls
- Sequential IDs with no ownership check: the McHire default password flaw combined a default admin password with an internal API that returned applicant records by a sequential
lead_id; researchers reported that decrementing the number returned other applicants' details, with more than 64 million records estimated to be reachable. - Checking only the user ID in the path: it fixes
/users/{id}/profilebut misses objects shared through teams, tenants or delegation, and objects referenced only in the body. OWASP calls this out as insufficient. - Checking the parent but not the child: the handler confirms the caller belongs to
orgIdand then loadsinvoiceIdglobally, so any invoice in the system can be read through any org the caller belongs to. - Read checked, write forgotten: GET has a check; PUT, PATCH, DELETE or the export job does not. OWASP's GraphQL
deleteReportsscenario is exactly this. - Trusting the internal network: internal services accept any object ID from any upstream caller, so one compromised service or over-privileged service account reaches every tenant's data.
- Relying on UUIDs: random IDs leak through shared links, logs, analytics and other endpoints that list objects; once known, an unchecked endpoint serves them.
Practitioner checklist
- Inventory every operation that accepts an object identifier, including GraphQL node fields, batch arrays and nested routes.
- Document the authorisation model per resource type: relationships that grant read, write, delete and export.
- Derive subject and tenant only from the validated credential, never from client-supplied fields.
- Make scoped lookups the default in repositories and add row-level security for tenant isolation where tables are shared.
- Return a uniform 404 for missing and unauthorised objects.
- Bind every API key, OAuth client and service account to a tenant and propagate the end user's identity between services.
- Run cross-user and cross-tenant authorisation tests in CI from an authorisation matrix and fail the build on mismatch.
- Replace public sequential identifiers with random ones on high-risk resources, as hardening only.
- Log denials with subject, client, tenant and object ID, and alert on enumeration patterns and cross-tenant attempts.
- Track coverage, cross-tenant attempts and time to fix as standing metrics.
Standards and references
- OWASP: API1:2023 Broken Object Level Authorization
- OWASP: API Security Top 10 2023
- OWASP: Application Security Verification Standard 5.0
- OWASP Cheat Sheet Series: Insecure Direct Object Reference Prevention
- OWASP Cheat Sheet Series: Authorization
- OWASP Cheat Sheet Series: Authorization Testing Automation
- OWASP Cheat Sheet Series: GraphQL
- MITRE: CWE-639 Authorization Bypass Through User-Controlled Key
Related NHI Mgmt Group resources: Top 10 API Security Risks · API Security Guide · Secure API Design Guide · GraphQL Security Guide · OWASP API Top 10 API3 Broken Object Property Level Authorization Guide