This article explains what IDOR, BOLA, and privilege escalation are, why they come from the same missing ownership check, and how a pentest finds them before an attacker does.
An IDOR finding comes back from a pentest, and engineering has a ready objection: the API runs on random UUIDs, not sequential numbers, so where’s the actual risk? There is one. A compliance questionnaire would flag it under one name, a pentester under another, the OWASP API Security Top 10 under a third, and underneath all three, it’s the same missing check.
Quick answer
- IDOR (Insecure Direct Object Reference): a raw ID sits in a URL or an API call, and whoever’s asking for it never gets checked against what it actually belongs to. OWASP files this under its #1 web risk, Broken Access Control (A01).
- BOLA (Broken Object Level Authorization): an API hands back or edits an object without confirming the requesting account owns it. OWASP’s API Security Top 10 puts this at #1 (API1:2023).
- IDOR vs. BOLA: one flaw, two names. Web applications call it IDOR. APIs call it BOLA.
- Privilege escalation: an account ends up with access nobody granted it. Horizontal privilege escalation reaches a peer’s data at the same privilege level. Vertical privilege escalation reaches functions built for a higher role.
- Further down: the comparison table, then the fix checklist.
Broken access control has held OWASP’s #1 Top 10 spot (the most critical risk category) since 2021, unchanged in the 2025 edition. IDOR, BOLA, and privilege escalation make up most of that category. A companion piece, Frontend Penetration Testing: How User Roles Create IDOR, BOLA, and Privilege Escalation Risk, covers the client side of this.
IDOR vs. BOLA: Same Flaw, Two Layers
At the web application layer, this gets called IDOR, Insecure Direct Object Reference. A URL or form field holds a raw reference somewhere, a user ID, an invoice number, an order number, and the account making the request never gets checked against what that reference actually points to. /api/orders/8841, requested normally, returns the account’s own order. Change the number to /api/orders/8842, someone else’s order, and if a 200 comes back with their data, the ownership check never ran in the first place.
BOLA names that same failure at the API layer. Here’s the part that surprises people: a guessable ID was never a requirement. /api/records/a3f1e9c2-88b4-4e21-9f6d-71c4a9e0d1a8 isn’t something anyone could guess. It doesn’t need to be guessed, though, only obtained, off a shared link, a support ticket, or an endpoint somewhere that lists records by ID. Whatever check would have stopped a sequential ID from working was never there for this one either. The identifier’s format was never the vulnerability, the missing check is.

Privilege Escalation: Horizontal and Vertical
Horizontal privilege escalation is what IDOR and BOLA usually produce. There’s no role change involved, just one account reaching data or actions that belong to an equal, not a higher role. Take two Managers on the same team. One edits a project that was never theirs, just by swapping the project ID in the request.
Vertical escalation points the other direction. A standard User calls an endpoint that was only ever built for Admin, and the call goes through. At the API layer, this failure has its own name, Broken Function Level Authorization (BFLA), ranked API5:2023 on the OWASP API Security Top 10.
These two often chain together. Password-reset tokens, session tokens, API keys, any of these can end up exposed through the same IDOR or BOLA flaw when the account behind them holds more privilege. Whichever one leaks, it’s enough on its own to log in as that account. From there, the attacker inherits its role, and a horizontal find just turned vertical.

Comparison Table
IDOR, BOLA, and both forms of privilege escalation trace back to the same three standards. Only the layer and the specific control change:
| Term | Where it applies | OWASP Top 10:2025 | API Security Top 10:2023 | ASVS 5.0 |
|---|---|---|---|---|
| IDOR | Web application | A01 Broken Access Control | API1 Broken Object Level Authorization | V8 Authorization |
| BOLA | API | A01 Broken Access Control | API1 Broken Object Level Authorization | V8 Authorization |
| Horizontal privilege escalation | Web app / API | A01 Broken Access Control | API1 Broken Object Level Authorization | V8 Authorization |
| Vertical privilege escalation (BFLA) | Web app / API | A01 Broken Access Control | API5 Broken Function Level Authorization | V8 Authorization |
Why It Keeps Happening: The Role-Boundary Math
Two accounts at the same privilege level. A generic admin-versus-user example. That’s about as far as most write-ups on this take it, and for a genuinely two-role app, it’s a fair picture. Most products don’t stay there for long, though. Add a billing admin, a support tier, a read-only auditor, and there’s a full matrix instead: Guest, User, Manager, Admin, whatever other tiers exist, each one crossed against every feature the app exposes.
Add a role and a new boundary comes with it, one that has to be enforced on its own. Four roles produce six boundary pairs. Ten roles produce forty-five. n(n-1)/2 is the formula behind it, and it outpaces the role count by a wide margin as roles get added.
Any one of those pairs can be the place where an IDOR or BOLA check got missed on a single endpoint, even with nine others done correctly. Hand-sampling a handful of endpoints won’t surface that. Coverage is the problem here, not skill, and it only gets bigger as the app adds roles or features.
Real-World Example: The McDonald’s McHire Breach
In June 2025, researchers Ian Carroll and Sam Curry found a way into McDonald’s hiring platform, McHire, built by Paradox.ai. A restaurant-owner admin panel still accepted the default login 123456:123456. From there, an internal endpoint took a parameter called lead_id, the ID of a single applicant’s chat with McHire’s screening bot, Olivia. The researchers’ own test lead sat around 64,185,742. Decrementing that number returned another applicant’s data. No ownership check stopped it.
Behind that one parameter sat names, emails, phone numbers, addresses, every form field an applicant had submitted, and an auth token that logged into the applicant’s own chat session, exposing the full conversation transcript. Carroll and Curry reported that the flaw, combined with the exposed admin account, put more than 64 million applicant records within reach. Paradox.ai later said only a handful of records were actually viewed during the disclosure; the researchers maintain the flaw made all 64 million technically reachable.
One field, one predictable number, and a boundary meant to separate applicants from each other never got checked. That’s not a theoretical finding. It’s a Fortune 500 hiring platform with an open door.
Scanners, Manual Pentests, and Where Continuous Pentesting Fits
Catching IDOR or BOLA means replaying one account’s request with another account’s ID or token and comparing the results. Some scanners can run multiple sessions and flag candidates, but a clean 200 with a well-formed JSON body looks the same whether the object belongs to the requester or to a stranger. Deciding whether that data should have been returned takes human analysis and logic.
Scanners still earn their place: they catch known CVEs and injection patterns the moment they ship, and their candidates give a tester leads to confirm. Combining manual and automated testing is a hybrid, and running it on a schedule is continuous pentesting: a human retests as the app changes, with scanning such as Wraith Security watching in between, so role-to-role boundaries get rechecked instead of tested once. The frontend pentest article compares each approach in full.
Fixing It: What Gets Checked
Each fix below has a direct test behind it, the same one a CYBRI engagement runs against a real account at every role level, not just admin and user.
- The fix: ownership checks run server-side on every request, not just at login. Tested directly: object IDs swapped between two same-role accounts to confirm ownership isn’t assumed.
- Deny by default, an explicit authorization check per object and per function. Tested directly: every higher-role endpoint called with a lower-role token, independent of what the interface exposes.
- Indirect or unpredictable references cut down guessability but don’t fix BOLA on their own, the check is what matters. Confirmed by making sure the decision happens server-side, not just hidden in the UI.
- Regression tests per role catch drift when a new endpoint ships. The same reason the permission matrix gets re-tested against the latest release.
Frequently Asked Questions
Do UUIDs or random IDs prevent IDOR or BOLA?
No. Harder to guess isn’t the same as impossible to reach, and that’s the whole gap here. The endpoint still has to check who owns the object. Skip that check, and an attacker just needs one valid ID, off a leaked link, a support ticket, another endpoint that lists them, and the result is identical to a guessable ID.
What’s the difference between IDOR and privilege escalation?
IDOR is a cause, privilege escalation is often the effect. An IDOR flaw hands over a reference an attacker can tamper with. What happens next is privilege escalation: reaching a peer’s data counts as horizontal, reaching a higher role’s data or functions counts as vertical.
Do I need this tested if my API or frontend was already pentested?
Depends what got tested. A frontend pentest usually maps client-side roles and interface behavior; an API pentest usually confirms an endpoint returns the right data for one account. Neither one, by default, replays that same request under a second account’s identity, and that comparison is exactly what catches IDOR, BOLA, and privilege escalation. The frontend pentest article covers that companion ground in full.
Can a vulnerability scanner find BOLA or IDOR?
Rarely. Finding it means comparing two authenticated sessions against each other, and most scanners only run one. Even one that did would still have no way to judge whether a gap it found should or shouldn’t exist, that call takes knowing what the app is supposed to allow, which is exactly what a manual tester brings and a scanner doesn’t.
How much does IDOR, BOLA, or privilege-escalation testing cost?
A web application pentest that covers this kind of testing starts around $5,000 and can run up to $30,000, with role count and whether an API is in scope pushing the number up. Contact CYBRI for a quote scoped to a specific app.