FlawOpen

Vulnerabilities/Broken Object Level Authorization (BOLA / IDOR)

Broken Object Level Authorization (BOLA / IDOR)

Critical CWE-639
Language: English Português (Brasil) Español Français Deutsch Русский 简体中文 日本語 हिन्दी 한국어 Bahasa Indonesia

Broken object level authorization, also called insecure direct object reference (IDOR), happens when an API confirms who the caller is and then fetches or changes a record chosen purely by an ID from the request. Change /api/invoices/1001 to /api/invoices/1002 and the server hands over another customer's invoice. It tops the OWASP API Security Top 10 because every endpoint that takes an ID is a candidate, and scanners cannot tell which records a user should see. The fix is the same in every stack: put the ID and the authenticated owner or tenant in the same query, take the owner only from the session, accept writes only through a schema that lists editable fields, and answer 404 for records that belong to someone else. Pick your language below for the exact ORM calls, vulnerable and fixed code, and tests that catch it.

Plain English Explainer (ELI5)

At a theatre cloakroom the attendant checks that you are wearing tonight's wristband, then hands over whichever coat matches the number you say out loud. Your ticket says 12, but you say 57 and walk out with a stranger's coat and the house keys in its pocket. Nobody checked that 57 was your ticket, only that you were a guest. An API with broken object level authorization works the same way: it checks that your login is valid, then loads invoice 1002 because the URL says 1002. The fix is for the attendant to search only the coats hung under your name, so every lookup asks for this number and this owner together.

Core Concepts & Subsystem Terms

BOLA / IDOR (CWE-639)
The API lets a client-supplied key (a path segment, query parameter or body field) select which record is read or changed, without checking that the record belongs to the caller.
Authentication is not authorization
A valid session, JWT or API key proves identity only. Every handler still has to decide whether this identity may touch this particular record.
Tenant-scoped query
Load records with the ID and the caller's organization or owner in the same WHERE clause, so another tenant's record is simply not found. The owner comes from the authenticated principal, never from the request.
Mass assignment of owner fields (CWE-915)
Binding a request body straight onto a stored record lets a client set owner_id, organization_id or the ID itself, moving records between tenants. Accept only a DTO or schema that lists the editable fields.
Unguessable IDs
UUIDs and random IDs make guessing harder but leak through links, logs, exports and other API responses. They reduce enumeration; they never replace the ownership check.

Step-by-Step Attack Flow

Step 1

The attacker signs in with a real account

A trial or low-privilege account passes every authentication check the API has.

Step 2

They swap an ID in a request

They replace their own ID with a neighbour's, or reuse IDs seen in exports, emails or other responses. Two test tenants with sequential IDs illustrate the case.

Step 3

The handler loads the record by ID alone

A primary-key lookup such as findById, db.get or findUnique returns the row whoever owns it.

Step 4

Data leaks or changes across tenants

The attacker reads, edits or reassigns other customers' records and can walk the whole ID range. The 2019 First American exposure is a separate incident; its reporting does not establish this exact authenticated API flow.

BOLA / IDOR guides by language

Python JavaScript Go Java

Engineering & System Hardening Checklist

Related reading

Supabase and Firebase row-level security bypass JWT algorithm confusion OAuth 2.0 redirect URI hijacking LangGraph JS Auth: Cross-User Store Access