Broken Object Level Authorization (IDOR)
Broken Object Level Authorization (BOLA), also called Insecure Direct Object Reference (IDOR), is an API flaw where the server confirms you are logged in but never checks that the object you requested actually belongs to you, so changing an ID in the request returns another user's data. It is ranked API1:2023, the number-one risk in the OWASP API Security Top 10.
Most teams assume that once a user has authenticated, their data is safe. BOLA breaks that assumption in the quietest way possible: the request looks completely legitimate, the user is genuinely logged in, and the only thing they changed was a number or identifier in the URL or request body. There is no malformed payload, no injection string, and no failed login to trip an alert. That ordinariness is exactly why BOLA sits at the top of the OWASP API Security Top 10 and why it slips past WAFs and traditional scanners that look for attack signatures rather than missing ownership checks.
Reviewed by Ameya Lambat
Security Research Contributor, Legba
Reviewed 2026-05-28 · Updated 2026-05-28
What it is
BOLA is a failure of object-level authorization: the access-control logic that should run, per object, every time an API acts on a record identified by user-supplied input. A typical endpoint such as GET /api/orders/1043 takes an object identifier (the order ID) from the request and returns the matching record. Authentication answers "who are you?" and the server gets that right. Authorization at the object level answers "are you allowed to touch THIS specific object?" and the server skips it. As a result, an authenticated attacker can substitute 1044, 1045, or any other valid identifier and receive records that belong to other tenants or users. OWASP documents this as API1:2023 Broken Object Level Authorization, and MITRE catalogs the underlying weakness as CWE-639 (Authorization Bypass Through User-Controlled Key), a child of the broader CWE-285 (Improper Authorization). IDOR is the older, near-synonymous term; per the CWE maintainers, IDOR is generally a subset of BOLA, which also covers indirect and nested object references such as GraphQL node IDs and path parameters.
If this stays open, the loss is rarely a single record. Because object identifiers are usually enumerable or guessable, one BOLA endpoint becomes a bulk-extraction tap: an attacker scripts a loop over IDs and exfiltrates an entire customer table, including PII, financial records, or health data, without ever triggering a credential alarm. OWASP places BOLA at API1:2023 precisely because it is both pervasive and trivially scriptable, and the same weakness has driven real-world bulk data exposures across e-commerce, automotive telematics, and document-storage APIs. For a regulated organization the downstream cost compounds: mandatory breach notification, regulatory penalties under GDPR or HIPAA, contractual SLA breaches with enterprise customers, and the slow erosion of trust that follows a headline reading "attacker downloaded every user's data by changing a number." What a security team actually wants here is not another raw scanner hit but a confirmed, evidenced finding showing exactly which object they reached that they should not have, so they can fix it before someone else runs the same loop.
At a glance
How attackers find and exploit it
- Map the API surface by capturing traffic from the web or mobile app through an intercepting proxy (Burp Suite, mitmproxy) and cataloging every endpoint that accepts an object identifier in the path, query string, body, or header.
- Profile the identifier scheme: note whether IDs are sequential integers (1042, 1043), short hashids, UUIDs, or composite keys, and whether any are leaked in responses, redirects, JWT claims, or email links that aid enumeration.
- Establish a baseline with a low-privilege authenticated account, recording the exact request and the legitimately owned object it returns, including response size and status code.
- Tamper with the object reference while keeping the valid session token intact: increment or decrement integers, fuzz UUIDs harvested from other responses, swap tenant identifiers, or replay a second user's known ID to test for missing ownership checks.
- Automate enumeration with a tool such as Burp Intruder or a custom script to sweep the identifier space, comparing responses to the baseline to flag any 200 OK that returns data belonging to another principal.
- Escalate beyond read: chain BOLA with mass assignment by adding fields like "role":"admin" or "owner_id" to PUT/PATCH bodies on the same object endpoint to modify or take over other users' records, then pivot to bulk exfiltration or destructive DELETE operations.
How to detect it on your surface
- Inventory every externally reachable API route that accepts an object identifier and document, per route, where the authorization check is supposed to occur in code.
- For each such route, test it with two separate authenticated accounts (User A and User B) and confirm that User A cannot retrieve, update, or delete an object owned exclusively by User B.
- Review object-fetch query patterns in source: flag direct lookups like Model.find(id) that are not scoped to the current principal (for example, currentUser.objects.find(id)) and are missing a tenant/owner predicate.
- Audit GraphQL resolvers and nested/expanded REST fields, since object-level checks are frequently enforced on the top-level entity but skipped on related objects reached through node IDs or embedded references.
- Correlate access logs for sequential or high-volume identifier access patterns from a single session or token, which indicate enumeration against an ID-bearing endpoint.
Detection signals
- An endpoint returns HTTP 200 with a full object body when supplied another user's identifier, instead of 403 Forbidden or 404 Not Found.
- Response bodies contain owner, tenant, account, or user fields whose value does not match the authenticated principal in the session token or JWT sub claim.
- Object identifiers in URLs and responses are sequential integers or short, predictable hashids that make adjacent records guessable.
- Server returns near-identical response sizes and timing across a contiguous range of IDs, signaling that records exist and are being served regardless of ownership.
- PUT/PATCH requests on an object endpoint succeed even when the body includes privilege or ownership fields (role, owner_id, account_id) that the user should not control, indicating BOLA combined with mass assignment.
- Access logs show a single token sweeping a monotonic ID range with consistent 200 responses.
Validate before you report
- Provision two test accounts in the same role tier (User A and User B) with no shared resources, and create at least one private object under each so ownership boundaries are unambiguous.
- As User A, capture a legitimate request to an object A owns and record the authorization token, the object identifier, and the exact 200 response as a true-negative baseline.
- Replay the identical request but substitute User B's object identifier while keeping User A's valid session token; a 200 response returning User B's data confirms a true-positive BOLA finding and should be captured as evidence (request, token principal, and the leaked object body).
- Repeat for non-GET methods: attempt PUT, PATCH, and DELETE against User B's object as User A to confirm whether the flaw permits modification or destruction, not only disclosure.
- Test mass-assignment overlap by adding controlled fields (for example owner_id or role) to the write body and verifying whether the server accepts them, which proves an account-takeover path rather than read-only exposure.
- Confirm scope by enumerating a small, bounded set of additional identifiers to verify the missing check is systemic to the endpoint rather than an artifact of one shared or seeded record.
What looks like this but isn't
- Confirm the second object is genuinely owned by a different principal and not a deliberately shared, public, or team-scoped resource; legitimately shared objects returning 200 are by design, not BOLA.
- Rule out test-data contamination where both accounts were seeded with the same fixture record or belong to the same tenant/organization, which would make cross-access expected.
- Verify the response actually contains the other user's data rather than a generic 200 with an empty body, a redirect to a login or error page, or a sanitized placeholder that exposes nothing sensitive.
- Check that authentication itself was enforced; an endpoint that is simply unauthenticated is a different finding (missing authentication) and should not be mislabeled as object-level authorization failure.
Remediation
- Enforce an object-level authorization check on every endpoint that accepts a user-supplied identifier, validating that the authenticated principal owns or is entitled to the specific object before any read, write, or delete.
- Derive identity from the trusted session or token (the JWT sub claim or server-side session), never from a client-supplied owner_id, and scope every database lookup to that principal (for example currentUser.orders.find(id) rather than Order.find(id)).
- Centralize authorization in a single policy layer or middleware (such as a policy engine, OPA, or framework guards) so checks cannot be forgotten per-route, and apply the same enforcement to nested objects and GraphQL resolvers.
- Adopt an explicit allowlist for writable fields on create/update operations to close the mass-assignment path that lets BOLA escalate to ownership or privilege changes.
- Use unpredictable, non-sequential identifiers (UUIDv4 or random tokens) as a defense-in-depth measure to frustrate enumeration, while treating this as a supplement to, never a replacement for, authorization checks.
- Add automated authorization tests to CI that run every ID-bearing endpoint with two accounts and assert cross-account access is denied, so regressions are caught before deployment.
- Return 404 rather than 403 for objects the user may not access where appropriate, so responses do not confirm the existence of records the attacker is enumerating.
Operational checklist
- Maintain a living inventory of all external API endpoints and flag every one that accepts an object identifier as requiring an object-level authorization check.
- Require, as a code-review gate, that every new object-fetch query is scoped to the authenticated principal and that no client-supplied identity field is trusted for authorization.
- Run two-account cross-access tests in CI/CD against every ID-bearing endpoint and block releases that allow unauthorized object access.
- Continuously monitor access logs and API gateway telemetry for sequential identifier enumeration and unusual high-volume object access per token, and alert on it.
- Schedule recurring authenticated API penetration testing and automated validation that specifically targets BOLA across REST and GraphQL, including nested and bulk endpoints.
- Keep the writable-field allowlist and authorization policy under version control and review it whenever an object's schema or roles change.
What to do next
BOLA is the most common and most quietly damaging API flaw on the external attack surface because it hides inside perfectly valid requests: the attacker is logged in, the payload is clean, and only an identifier changed. The single most reliable way to find it is to test every ID-bearing endpoint with two accounts and confirm one cannot reach the other's objects. Do not wait for an audit cycle. Pull your inventory of externally reachable, identifier-accepting API routes today and run the two-account test against the highest-value data endpoints first, then close any confirmed gap with a per-object ownership check before someone scripts the loop for you.
Methodology
Each finding-type guide is built from Legba Recon's real detection and validation logic, reviewed by a named security contributor, and cited against primary sources such as OWASP, CISA, NIST, and MITRE. We update pages when the underlying guidance changes. See our contributors and company.
FAQs.
References.
- 01API1:2023 Broken Object Level AuthorizationOWASP API Security Project
- 02
- 03
- 04Insecure Direct Object Reference Prevention Cheat SheetOWASP Cheat Sheet Series
