What is BOLA (IDOR)? The number one API vulnerability, with examples and how to prevent it

By Kike Gandia · Co-Founder & CEO, OSCP

BOLA (Broken Object Level Authorization) is the flaw that occurs when an API checks who you are but not whether the object you ask for is yours. Changing an identifier in the request —an invoice number, an order ID, a user ID— is enough to read, modify or delete someone else's data. It is the number one risk in the OWASP API Security Top 10 and the modern version of what for years was called IDOR (Insecure Direct Object Reference). This guide explains the difference between the two terms, what it looks like in real requests, why scanners almost never find it, how it is tested and which measures genuinely fix it.

What BOLA is and what IDOR is

An API receives an authenticated request —with a valid session, token or key— that includes an object identifier: GET /api/v1/invoices/1043. If the server checks that the token is valid but not that invoice 1043 belongs to the token's user or company, any registered user can request 1044, 1045 and all the rest. That is a BOLA.

The formal definition has three parts:

  • The attacker is authenticated or the feature is public: they do not need to steal anyone's credentials, their own account is enough.
  • The object is identified by a value the client controls: an ID in the path, a parameter, a body field, a header.
  • The object-level authorization check is missing: the server does not verify that this user may perform this action on this specific object.

In MITRE's weakness classification it corresponds to CWE-639, “Authorization Bypass Through User-Controlled Key”, and in the 2023 OWASP API Security Top 10 it is API1:2023. The impact ranges from exposing other users' data to modifying or deleting it and, in the worst cases, taking over accounts.

BOLA vs IDOR: are they the same?

In practice they are used as synonyms, and a pentester reporting either one is talking about the same flaw. The difference lies in origin and emphasis:

AspectIDORBOLA
Full nameInsecure Direct Object ReferenceBroken Object Level Authorization
OriginA category in early editions of the OWASP Top 10 for web applications; now part of Broken Access ControlNumber one risk in the OWASP API Security Top 10 since its first edition, in 2019
Focuses onThe reference: the identifier that is exposed and can be tampered withThe cause: the missing authorization check on the object
Usual contextClassic web applicationsREST and GraphQL APIs, mobile apps and microservices

The change of name is not cosmetic. “Insecure direct reference” invites you to think the problem is the identifier and that hiding it or making it unpredictable fixes it. “Broken object-level authorization” makes clear where the flaw is and where the fix belongs: on the server, on every access to every object.

What a BOLA looks like in real requests

The vulnerable identifier can travel in any part of the request, and it is not always a number:

Where the identifier isExampleWhat the attacker gets
PathGET /api/v1/orders/58213Other customers' orders, addresses and phone numbers
Query parameterGET /api/documents?customerId=311Another company's documents
Request bodyPOST /api/transfers with "sourceAccount": "GB29…"Operations ordered on someone else's account
HeaderX-Tenant-Id: 27A jump into another customer's data in a multi-tenant SaaS
Nested resourceGET /api/companies/12/employees/88Reading another company's employee because only the company in the path is checked
GraphQLa query with order(id: "…")The same as in REST, often in nested resolvers nobody reviewed
FilesGET /downloads/contracts/2024-117.pdfDownloading other people's contracts or receipts
Write operationsPUT, PATCH or DELETE on /api/addresses/9921Modifying or deleting another user's data
Batch operationsGET /api/invoices?ids=1001,1002,1003Extracting hundreds of objects in a single request

Two variants deserve special attention. Write variants, because the impact moves from confidentiality to integrity: changing another account's email usually ends in taking over that account. And multi-tenant variants, where the object belongs to a customer organisation: a single BOLA exposes every customer on the platform at once.

Why it is so common

No team decides not to check authorization. BOLA appears because of how APIs are built:

  • Frameworks solve authentication, not ownership. A middleware validates the token on every route; checking that order 58213 belongs to the user depends on the business model and has to be written endpoint by endpoint.
  • Testing is done with a single user. The developer tests with their own account, sees their own data and everything works. Without a second user, the flaw is invisible.
  • Identifiers travel to the client. The mobile app or the SPA needs the IDs to request objects, so the attacker sees them and can change them.
  • Microservices trust whoever calls them. The gateway authenticates and the internal service assumes that, if the request arrived, it is authorized for any object.
  • Nested resources and new routes get forgotten. GET /orders/{id} is protected, and GET /orders/{id}/invoice is added later without the same check.
  • Direct database access from the client. On backend-as-a-service platforms, if row-level security policies are not enabled or are badly written, the result is a BOLA across the whole database. It is one of the flaws AI-generated applications repeat most often.

Why scanners almost never find it

To an automated scanner, a 200 response with data is a correct response. It does not know that order 58213 belongs to another customer, because that is business logic, not a technical pattern like an injection or a missing header.

Some tools let you configure two sessions and compare responses, and they help on small, well-documented APIs. But they still need someone to define which objects belong to whom, which roles exist and what each one should see, and they fail in the most interesting cases: identifiers that only appear in responses from other endpoints, nested objects, write operations with side effects or objects legitimately shared between users.

That is why BOLA is at the same time the most frequent flaw in API penetration tests and one of the ones that most often slips past the automated analysis companies already have.

How a BOLA is tested

The test is methodical rather than sophisticated. The essential thing is to have at least two users per role, ideally in two different organisations if the application is multi-tenant, and to walk through the whole API with both:

StepWhat is doneWhat to look for
MapWalk through the application as user A capturing every request, or start from the OpenAPI specificationEvery endpoint that receives an identifier
Collect identifiersNote user B's object IDs appearing in responses, shared links, exports or emailsValid identifiers of other users' objects, even if they are not predictable
SwapReplay each of A's requests replacing A's identifiers with B's, using A's tokenResponses with data from, or effects on, B's objects
Every methodTest read, update and delete, not just GETWrite BOLAs, with higher impact
No sessionReplay the requests without a token or with an expired oneEndpoints that should require authentication and do not
Compare responsesTell apart 403, 404, empty 200 and 200 with dataPartial checks or filtering done only in the response

Extensions for intercepting proxies that replay every request with another user's session automate much of the swapping, but interpretation is still manual: some objects are legitimately shared and some responses look correct when they are not.

The OWASP testing guide covers this procedure as WSTG-ATHZ-04, “Testing for Insecure Direct Object References”.

One detail that gets overlooked: an unpredictable identifier, such as a UUID, does not remove the BOLA. It only changes the question from “can I guess the ID?” to “can I obtain the ID?”, and the answer is usually yes: it appears in other API responses, in shared URLs, in notifications or in browser logs.

Real cases

The best-documented case of this flaw is First American Financial, a US title insurance company, because the SEC published its investigation in detail:

AspectWhat the official source says
The flawDocuments in its EaglePro web application were served at URLs with a sequential document number; changing the digits in the URL showed documents from other transactions
What was exposedOver 800 million document images, some dating back to 2003, containing personal and financial data
When it was knownIts own security staff had found it months earlier, in an internal penetration test in January 2019; in May 2019 a cybersecurity journalist alerted the company and the case became public
SEC penalty (2021)$487,616, not for the vulnerability itself but because the information did not reach the executives responsible for what the company disclosed to investors
NYDFS penalty (2023)$1 million for violating the New York financial regulator's cybersecurity regulation

It is a classic IDOR in a web application rather than an API, but the mechanism is exactly the same. And it leaves two lessons that go beyond the technical: the flaw was found months before it became public, so the problem was not detecting it but prioritising and fixing it; and a BOLA that exposes customer data is a leadership issue, not just a development team one.

There are large-scale API incidents whose exact mechanism is not known. In January 2023, T-Mobile told the SEC that an attacker had obtained data from some 37 million customer accounts “through a single API without authorization”, without detailing the type of flaw. That is the rule rather than the exception: official disclosures rarely explain the technical cause, which is why lists of “IDOR breaches” circulating without sources deserve scepticism.

How to fix it properly

The fix has to live on the server, on every access to every object. These measures, from most to least important:

MeasureWhat it solvesWhat it does not solve
Per-object authorization check in a central layerEnsures every read and write verifies user, object and actionNothing, if some endpoints bypass that layer
Queries scoped to the user or tenantLooking up the order by its ID and the current user's at the same time, so another user's ID simply does not existObjects shared between users, which need an explicit rule
Row-level security in the databaseA second barrier even if the application forgets a filterComplex authorization logic that does not translate well into database policies
Identifier-free endpoints for your own resourcesGET /api/me/profile instead of GET /api/users/77 removes the tamperable parameterResources the user is genuinely allowed to choose
Unpredictable identifiers (UUIDs)Make mass enumeration harderDo not replace the check: the ID can leak
Uniform responseReturning 404 both when the object does not exist and when it is not yours avoids confirming which IDs existThe underlying flaw, if the check is missing
Enumeration detectionAlerting on many requests to sequential identifiers or other users' objectsSlow attacks or ones targeting a few objects

The design rule that prevents the most BOLAs is deny by default: if a new endpoint does not declare its access policy, it does not ship. And the one that helps most in review: give the check a searchable name in the code, instead of conditions scattered across every controller.

How to stop it coming back: testing and review

A fixed BOLA comes back easily in the next endpoint. What works to prevent that:

  • Authorization tests in the pipeline. For every endpoint that receives an identifier, a test that creates two users and checks that A cannot read, modify or delete B's data. It is cheap, fast and catches the regression before production.
  • Code review with a fixed question. In every pull request that adds or changes an endpoint: where is it checked that this object belongs to this user?
  • Endpoint inventory. An up-to-date OpenAPI specification shows which endpoints receive identifiers and lets you check they all have their test.
  • Watch BOLA's siblings. The OWASP API Top 10 has two other authorization risks: at property level (fields that should not be returned or accepted) and at function level (admin endpoints reachable by regular users). They are tested the same way, with several users and roles.
  • A pentest after access model changes. New roles, shared accounts, multi-tenancy, a partner API or a new mobile app are the moments when most BOLAs appear.

FAQ

Are BOLA and IDOR the same thing?

In practice, yes: they describe the same flaw. IDOR is the classic web application name and focuses on the identifier being tampered with; BOLA is the OWASP API Security Top 10 name and focuses on the cause, the missing authorization check on the object.

Does using UUIDs instead of numeric IDs prevent BOLA?

No. It makes identifiers harder to guess, but it authorizes nothing: if the attacker obtains the UUID of someone else's object —in another API response, a shared link or a notification— the flaw is still there. It is a complementary measure, never the fix.

Why didn't our vulnerability scanner detect it?

Because detecting it requires knowing who owns each object, and that is business logic. To a scanner, a response with data is a correct response. It is found by testing with at least two users per role and swapping their identifiers on every endpoint.

How severe is a BOLA?

It depends on what data it exposes and whether it allows writes. A read BOLA on personal or financial data, or any that allows modifying or deleting other users' data or taking over accounts, is usually rated high or critical. One on low-sensitivity, non-enumerable data may be medium.

If we find a BOLA, do we have to report it to the data protection authority?

Only if there was a breach: if someone unauthorised accessed personal data, the GDPR requires notification within 72 hours unless it is unlikely to result in a risk to individuals. If the flaw is found in a pentest and the logs show nobody exploited it, there is no breach to notify, although the analysis should be documented. Without sufficient logs, that conclusion is much harder to support.

How do we prevent it in development without slowing the team down?

With a centralised authorization layer, queries that always include the current user or tenant, and an automated two-user test per endpoint in the pipeline. They are small changes if adopted early and they stop most BOLAs before they reach production.

Related service

API penetration testing with per-user and per-role authorization testing

Related content

Sources

Test my API for BOLA and other authorization flaws