Broken Access Control: Why IDOR and BOLA Top Every List
Why broken access control is the number one web risk in OWASP data, how IDOR and BOLA flaws lead to real breaches, why scanners miss them, and how to fix them.
By MD-5 Updated 6 min read
Broken access control means a user can do something they shouldn’t: read another customer’s invoice, call an admin-only endpoint, or edit a record in someone else’s account. It is the most common serious flaw in modern web applications, and it’s the one automated scanners are worst at finding.
This article covers what the data says, the main forms the flaw takes, what real breaches it has caused, and how to prevent it.
What the data says
The OWASP Top 10:2025 (opens in a new tab) ranks broken access control first, as it did in 2021. The numbers behind that ranking come from application testing data contributed by security firms:
- 100% of the applications tested had some form of broken access control.
- The category maps to 40 CWEs and recorded 1,839,701 occurrences, more than any other category.
- It is linked to 32,654 CVEs, the second-highest count in the list.
- Server-side request forgery (SSRF), a separate category in 2021, is now counted inside it.
The API equivalent tells the same story. The OWASP API Security Top 10 (2023) (opens in a new tab) puts three authorization flaws in its list, with broken object level authorization (BOLA) at number one.
Exploitation data agrees. In our analysis of CISA’s Known Exploited Vulnerabilities catalog, authentication and access-control weaknesses made up about one in five of the vulnerabilities added in 2026, up from around one in seven in 2023.
The forms it takes
Access control answers two questions on every request: who is this? (authentication) and are they allowed to do this, to this object? (authorization). The flaws below are all failures of the second question.
IDOR and BOLA: the wrong object
An insecure direct object reference (IDOR), called broken object level authorization (BOLA) in API security, is when the application takes an identifier from the request and fetches that object without checking it belongs to the caller.
GET /api/v2/invoices/10482 HTTP/1.1
Authorization: Bearer <token for customer 207>
If invoice 10482 belongs to customer 311 and the server returns it anyway, that’s BOLA. Change the number and you walk through every invoice in the system.
Broken function level authorization: the wrong action
The user reaches a function their role shouldn’t have. A “viewer” calls DELETE /api/projects/7, or a normal user loads /admin/users because the only protection was hiding the menu link. The OWASP API list calls this BFLA (API5).
Property level flaws: the wrong fields
The user can read or write fields they shouldn’t. Two common versions:
- Excessive data exposure: the API returns the whole database row (password hash, internal notes, other users’ email addresses) and relies on the front end to hide it.
- Mass assignment: the API binds every field in the request body to the model, so adding
"role": "admin"or"account_id": 311to a profile update works.
OWASP groups these as broken object property level authorization (API3).
Tenant isolation failures
In multi-tenant SaaS, any of the above that crosses between customer organizations. These are the findings that end up in breach notifications, because one tenant can read every other tenant’s data.
What it looks like in real breaches
Access-control flaws are rarely sophisticated. They are just unchecked.
- First American Financial (2019). A document link contained a sequential ID, and the server returned any document for any ID with no login required. About 885 million records, including bank statements and Social Security numbers, were exposed going back to 2003 (opens in a new tab). The SEC later fined the company $487,616 (opens in a new tab) over disclosure failures, and New York’s financial regulator imposed a $1 million penalty (opens in a new tab).
- USPS Informed Visibility API (2018). An API let any logged-in user query other users’ account details, affecting around 60 million users (opens in a new tab). The flaw had been reported to the Postal Service a year before it was fixed.
In both cases the fix was one ownership check on the server.
Why scanners miss it
A vulnerability scanner can tell that /api/v2/invoices/10482 returned 200 OK. It can’t tell whether it should have. That depends on business rules the scanner doesn’t know: which customer owns the invoice, what a “viewer” is meant to do, which tenant a record belongs to.
Finding these flaws takes a tester who understands the application’s rules and checks each one. This is the core of the difference between penetration testing and vulnerability scanning, and it’s why the OWASP data above comes from application testing rather than automated scanning alone.
How testers find it
A thorough access-control test is methodical rather than clever:
- Build a permission matrix. List every role, every object type and every action, and write down what each role should be able to do.
- Use two accounts per role. User A and user B in the same role, plus accounts in different tenants. Without a second account, you can’t test whether A can reach B’s data.
- Replay every request as someone else. Capture a request made by user A, then send it with user B’s session, with a lower role’s session, and with no session at all. Compare the responses.
- Swap every identifier. IDs in paths, query strings, JSON bodies, headers and GraphQL arguments. Non-sequential IDs (UUIDs) slow this down but don’t stop it: IDs leak through other responses, emails, exports and logs.
- Try other methods and versions. If
GETis checked, tryPUT,PATCHandDELETE. If/api/v2/is checked, try/api/v1/. - Add fields. Send properties the form doesn’t show (
role,owner_id,is_verified) and see which ones stick.
Tools help with the repetition (Burp Suite extensions such as Autorize replay requests with a second session automatically), but deciding what should happen stays a human job.
How to fix it
The durable fixes are architectural. Patching individual endpoints leaves the next endpoint exposed.
-
Deny by default. Every route requires an explicit authorization rule. A new endpoint without one fails closed, not open.
-
Enforce on the server, in one place. A central policy layer or middleware, not checks copied into each handler. The front end hiding a button is not access control.
-
Scope queries to the caller. Look up objects by
idand the current user’s account or tenant from the session, never an account ID taken from the request.SELECT * FROM invoices WHERE id = $1 AND customer_id = $2 -- $2 from the session -
Allow-list writable fields. Bind only the properties a user may change; never pass the raw request body to the model.
-
Return only what’s needed. Serialize responses through explicit schemas, so internal fields can’t leak.
-
Test authorization like a feature. For each endpoint, an automated test that calls it as another user and another tenant and expects
403or404. These catch regressions that would otherwise ship silently. -
Log denials. A burst of 403s across sequential IDs is someone enumerating. You want to see it.
Random identifiers are worth using as defense in depth, but they are not the fix. The fix is the ownership check.
A quick self-check
If you can’t answer yes to these, access control is worth testing:
- Is there a single place in the code where authorization is decided?
- Does every database query for user-owned data include the owner or tenant from the session?
- Do you have automated tests that try to read another user’s data?
- Would you notice someone requesting 10,000 sequential IDs?
A web application penetration test or API penetration test checks every role and object this way, and reports each flaw with the exact request that proves it. You can see what that looks like in our sample report, whose main finding is an IDOR.
Sources
- OWASP, A01:2025 Broken Access Control (opens in a new tab), OWASP Top 10:2025.
- OWASP, API Security Top 10 (2023) (opens in a new tab).
- OWASP, Authorization Cheat Sheet (opens in a new tab).
- B. Krebs, First American Financial Corp. leaked hundreds of millions of title insurance records (opens in a new tab), May 2019.
- US SEC, SEC charges issuer with cybersecurity disclosure controls failures (opens in a new tab), June 2021.
- Cybersecurity Dive, New York reaches $1M breach settlement with First American Title Insurance (opens in a new tab).
- B. Krebs, USPS site exposed data on 60 million users (opens in a new tab), November 2018.
Published by
MD-5 · Certified cybersecurity services
Articles are researched and written in-house and link to their primary sources (standards, advisories, papers and public datasets) so you can check every claim. Spotted an error? Email [email protected] and we’ll correct it.