📋 What Is It?
Broken Object Level Authorization (BOLA) is the failure to check that the authenticated caller is actually entitled to the specific object they asked for. The endpoint takes an identifier from the request - a path segment, a query parameter, a body field - and loads that record without ever asking "does this object belong to this caller?". Authentication answers who you are; object level authorization answers which rows you may touch. BOLA is what happens when an API only does the first.
It ranked #1 in 2019 and stayed #1 in 2023, because it is both the easiest flaw to exploit - change a number in a URL - and the hardest to catch with scanners, which cannot know whose data /orders/1043 should be.
⚠️ Common Exploits
- ID Enumeration:
GET /api/orders/1042→1043, sequential walk of every record - Client-Supplied Identity: server trusts
user_idfrom the body or query instead of the token - Nested Objects: parent checked, child (
/accounts/9/cards/77) is not - Write Paths Forgotten:
GETis guarded,PUT/DELETEon the same id is not - Second Channel: the web route checks ownership, the mobile or partner route does not
- Export & Batch Endpoints: report/bulk routes accept arbitrary id lists
🔴 Attack Flow
↓
2. Identify every request carrying an object id
↓
3. Replace the id with another user's (increment, or harvest from a listing)
↓
4. Observe a 200 with someone else's data - the check was never made
↓
5. IMPACT: Script the range and exfiltrate every record in the table!
❌ Vulnerable Code
✅ Secure Code
✓ Prevention Checklist
- Derive the caller's identity only from the verified session or token
- Check ownership/entitlement on every object, on every request
- Push the check into the data-access layer so it cannot be forgotten
- Use a single, central authorization policy - never per-handler ad hoc code
- Validate nested and related objects, not just the top-level one
- Cover write and delete paths, not only reads
- Return 404 rather than 403 to avoid confirming an object exists
- Prefer random UUIDs over sequential integers (defence in depth)
- Apply the same rules to bulk, export, and GraphQL resolvers
- Write automated tests that fetch another tenant's object and expect 404
🔍 Detection & Tools
Testing:
Monitoring:
How to Test:
- Create two accounts; replay user A's requests with user B's token
- A 200 means BOLA; a 404/403 means the check exists
- Walk sequential ids and watch for successful responses
- Repeat for
PUT,PATCH,DELETE, and bulk endpoints - Try nested resources whose parent you legitimately own
🌍 Real-World
These are documented incident classes - recurring patterns rather than single CVEs.
- Sequential id scraping: a mobile back end exposes
/api/v1/users/{id}; a script walking the range harvests names, emails and phone numbers for the whole user base in hours. - Client-supplied account identifiers: a fintech endpoint accepts the source account in the request body, letting an attacker move funds from accounts they do not own.
- Documents by direct reference: an e-signature or invoicing API returns any document id to any authenticated user, exposing contracts across tenants.
- Second-channel gaps: a hardened web API sits alongside a legacy partner API on the same data with no per-object checks at all.
📌 Quick Tips
- DO NOT trust an id, tenant, or role sent by the client
- DO NOT assume authentication implies authorization
- DO NOT rely on unguessable ids as the control
- DO scope every query by the caller's identity
- DO centralise the policy and reuse it everywhere
- DO test cross-tenant access in CI on every build
📜 Compliance
Related Standards:
- OWASP API Security Top 10 2019 - API1
- Maps to API1:2023 (unchanged, still #1)
- CWE-639 Authorization Bypass Through User-Controlled Key
- CWE-284 Improper Access Control
- CWE-566 Authorization Bypass Through User-Controlled SQL Primary Key