Broken Object Level Authorization (BOLA): Definition, Examples, and How to Fix It
Broken Object Level Authorization (BOLA) is an access control flaw where a user can read or modify objects (records/resources) they shouldn’t be able to by manipulating object identifiers (IDs) in requests. It’s the API-centric cousin of IDOR (Insecure Direct Object Reference), and it shows up when authorization is not enforced per object on the server.
Broken Object Level Authorization (BOLA) is one of the most common API security failures: the app verifies you’re logged in, but doesn’t verify you’re allowed to access that specific object (order, invoice, profile) referenced by an ID. In practice, this becomes IDOR-style data exposure or tampering by changing an identifier in the request—often with extremely low effort and high impact.
How BOLA works (authentication vs authorization)
BOLA is easiest to understand by separating authentication from authorization:
- Authentication answers: “Who are you?” (session cookie, JWT, API key)
- Authorization answers: “Are you allowed to access this specific thing?”
With BOLA, the system typically does the first part correctly—your request is authenticated—then fails the second part by trusting that the object ID you provided is one you’re allowed to access.
Common request pattern
A client requests an object by ID:
GET /api/orders/12345GET /api/users/7788PATCH /api/invoices/9001
A vulnerable server implementation does something like:
- Extract user identity from token/cookie.
- Load the object by ID.
- Return/modify it—without confirming the user has rights to that object.
An attacker (or curious user) changes the ID to another value (often sequential or guessable), and the server returns someone else’s record.
Why BOLA happens in practice
BOLA tends to emerge from “happy-path” development and assumptions:
- Developers assume the UI will only request objects that belong to the user.
- Authorization checks are done only at the “endpoint” level (e.g.,
role=usercan call/api/orders/{id}), not at the object level (e.g., this user can only access orders whereorder.customer_id == user.id). - Microservices or data layers expose convenient
getById(id)methods that are reused everywhere without passing user context for authorization. - Teams rely on “unguessable IDs” (UUIDs) as a security measure. UUIDs reduce guessing but do not replace authorization.
- Multi-tenant systems forget tenant scoping (missing
tenant_idfilters), causing cross-tenant data exposure.
What “good” looks like
Proper object-level authorization typically looks like one (or more) of these server-side patterns:
- Ownership checks:
object.owner_id == current_user.id - Tenant scoping:
object.tenant_id == current_user.tenant_id - Access control lists (ACLs): current user appears in object permissions
- Policy enforcement: centralized authorization layer (policy engine or framework middleware) evaluates access rules consistently
If you’re implementing centralized authorization with a policy engine, also see: How to secure Open Policy Agent (OPA) deployments.
Minimal vulnerable vs fixed logic (pseudocode)
# Vulnerable (BOLA): checks authN, not authZ per object
def get_order(order_id, current_user):
order = db.orders.get_by_id(order_id)
return order # No ownership/tenant check
# Fixed: enforce object-level authorization
def get_order(order_id, current_user):
order = db.orders.get_by_id(order_id)
if order is None:
return 404
if order.customer_id != current_user.id:
return 403
return order
A more robust pattern avoids fetching unauthorized objects at all:
-- Fixed (preferred): authorization in the query
SELECT *
FROM orders
WHERE id = :order_id
AND customer_id = :current_user_id;
This reduces the risk of “accidental” leaks through logging, error messages, or side-channel differences.
What BOLA looks like during testing
If you’re testing for BOLA, you typically:
- Authenticate as User A.
- Fetch an object you own (e.g., order
12345). - Change the ID to an object you don’t own (e.g.,
12346). - Observe whether the API returns
200 OKwith data, or allows modification.
Example with curl (replace token and IDs):
# As User A, legitimate access
curl -sS -H "Authorization: Bearer $TOKEN_A" \
https://api.example.com/v1/orders/12345
# Try a different object ID (potential BOLA)
curl -i -H "Authorization: Bearer $TOKEN_A" \
https://api.example.com/v1/orders/12346
Expected secure behavior: 403 Forbidden (or 404 Not Found to reduce object enumeration, depending on your design).
Log patterns that may indicate exploitation
Look for repeated requests differing only by object identifiers:
- Many sequential IDs in a short time window
- Same IP/user token hitting multiple object IDs
- 403/404 spikes on object endpoints, or worse, 200s for many IDs
Example (conceptual) indicators:
GET /api/orders/12340 200
GET /api/orders/12341 200
GET /api/orders/12342 200
GET /api/orders/12343 200
...
If you see this pattern with a single account, treat it as a potential enumeration/exfiltration attempt.
When you’ll encounter BOLA (common scenarios)
BOLA is common anywhere an application exposes object identifiers and the backend doesn’t enforce per-object checks consistently.
1) REST APIs and “CRUD” endpoints
Endpoints that map directly to database records are prime targets:
/users/{id}/documents/{docId}/tickets/{ticketId}/invoices/{invoiceId}
If your access control is implemented only as “authenticated users can call this endpoint,” you’re at risk.
2) Mobile apps and single-page applications (SPAs)
Mobile and SPA clients frequently call APIs directly and store object IDs locally. Attackers can:
- intercept and modify requests (proxy tools)
- replay requests with different IDs
- call APIs outside the app using captured tokens
Because the client is untrusted, any “authorization logic” embedded in the UI is not a control.
3) Multi-tenant SaaS (cross-tenant leakage)
A classic BOLA variant is missing tenant scoping:
- User from Tenant A can access Tenant B’s resources by changing an ID
- Resource queries don’t include
WHERE tenant_id = current_tenant
This is especially damaging in SMB-focused SaaS: it becomes a reportable data breach quickly.
4) GraphQL APIs (resolver-level authorization gaps)
GraphQL encourages flexible queries, but authorization must still be enforced in each resolver or via robust middleware. BOLA appears when:
- resolvers fetch objects by ID without verifying ownership/tenant
- “nested” objects are returned without checking access (e.g.,
user(id: X) { invoices { ... } })
5) Internal APIs and microservices
BOLA isn’t limited to “public” APIs. It can appear between services when:
- a service assumes upstream already validated authorization
- internal APIs are reachable from compromised workloads
- developer tooling or admin panels call internal endpoints directly
A good rule: every service that can return customer data should enforce authorization, not just the edge gateway.
6) “Hidden” object references (not just path IDs)
BOLA can occur via identifiers in many places:
- query parameters:
GET /api/download?fileId=... - request bodies:
POST /api/transfer { "fromAccountId": ..., "toAccountId": ... } - headers or cookies carrying object context
- indirect references (e.g., “short links” mapped to objects)
Don’t limit your controls to route parameters—validate all object references.
How to fix BOLA (practical mitigations)
Enforce server-side object authorization everywhere
For every endpoint that accepts an object identifier (path, query param, body), enforce at least one of:
- ownership checks
- tenant scoping
- explicit permission checks (ACL / RBAC / ABAC)
- policy-engine enforcement
Deny by default: if your policy can’t explicitly allow access, the result should be forbidden.
Put authorization into the data access pattern
Whenever feasible, write queries that are scoped to the authenticated principal:
WHERE id = :id AND tenant_id = :tenantWHERE id = :id AND owner_id = :user_id
This reduces the chance of accidentally returning an object before authorization is applied.
Use consistent error handling (403 vs 404)
- Use 403 Forbidden when you want clients to know the object exists but access is denied.
- Use 404 Not Found when you want to reduce object enumeration (common for public APIs).
Pick one approach per resource type and apply it consistently to avoid leaking existence via error differences.
Add automated tests for object-level authorization
For each object endpoint, build tests that assert:
- User A can access object A1
- User A cannot access object B1
- Cross-tenant access is blocked (if multi-tenant)
- Write operations (PATCH/DELETE) are also blocked, not just reads
Monitor for enumeration patterns
Alert on request patterns that are typical of BOLA exploitation:
- sequential ID scanning
- high rate of 403/404 on object endpoints
- many distinct object IDs accessed by one account/session
Quick prevention checklist (implementation-focused)
- Enforce authorization server-side for every object access (read/write).
- Scope every data query by owner_id and/or tenant_id (deny-by-default).
- Centralize authorization logic (policy/middleware) to avoid “one-off” fixes.
- Use 403/404 consistently; avoid leaking existence via error differences.
- Add tests: for each endpoint, verify User A cannot access User B objects.
- Monitor for ID enumeration patterns and alert on anomalies.
Recommended tools (optional, defense-in-depth)
While BOLA is primarily an application-logic issue (fixed in code and policy), defense-in-depth can still help reduce account takeover paths that make exploitation easier. If you want a reputable password manager to reduce credential reuse risk across your org, consider 1Password: Try 1Password →. For endpoint malware protection and cleanup support, Malwarebytes can be a practical layer: Get Malwarebytes →. (These won’t “solve” BOLA, but they can reduce the likelihood and impact of compromised accounts being used to probe APIs.)
This article may contain affiliate links. We earn a commission on qualifying purchases at no extra cost to you.
Related terms
The broader web-app concept where direct references to objects (IDs, filenames) allow unauthorized access. BOLA is effectively IDOR framed as an authorization failure at the object level, especially in APIs.
The umbrella category for authorization failures (including BOLA, privilege escalation, missing function-level checks, etc.).
The defensive requirement—deciding access for each individual resource instance, not just per endpoint.
When users can access actions/functions they shouldn’t (e.g., calling admin endpoints), even if object checks exist.
Ensuring requests are constrained to the requester’s tenant, often with tenant_id enforcement across all queries.
Accessing another user’s peer resources (e.g., reading someone else’s invoice). BOLA frequently enables this.
Accessing higher-privilege functions (admin operations). Not the same as BOLA, but often coexists in weak access control designs.
BOLA is consistently highlighted by OWASP as a leading API risk because it’s common, easy to exploit, and high impact.