Across every engagement we have recorded, broken authorization is the largest single class of serious finding we report — 173 high-and-critical findings, against 128 for every variety of cross-site scripting combined.
That is the number people quote back to us. It is also the least interesting thing in this page, because volume is not what makes authorization worth writing about.
The number that actually matters
Ask a different question: not how many, but how bad, when one is present.
| Critical | High | High or critical | |
|---|---|---|---|
| Authorization findings | 21% | 34% | 55% |
| All findings | 4% | 11% | 16% |
More than half of the authorization bugs we find are high or critical, against one in six for findings generally. One in five is critical, against one in twenty-five.
Put plainly: when we find an authorization bug, it is roughly five times more likely to be critical than an average finding. 22% of every critical finding we have ever reported is an authorization failure.
This is why we care about the class disproportionately. Not because there are many — there are fewer of these than there are missing security headers. Because they are almost never minor. A missing header is a weakness. A broken authorization boundary is usually somebody reading somebody else’s data.
What the class actually contains
“Broken authorization” covers several distinct failures that get lumped together. They are not the same bug and they do not have the same fix.
| Finding | Times found | High or critical | Rate |
|---|---|---|---|
| Authorization bypass via IDOR | 106 | 56 | 53% |
| Bypassing authorization schema, vertically | 82 | 49 | 60% |
| Bypassing authorization schema, horizontally | 59 | 42 | 71% |
| Anonymous authorization bypass | 19 | 6 | 32% |
| Broken access control | 15 | 12 | 80% |
| Authorization bypass by forceful browsing | 11 | 5 | 45% |
| Mass assignment | 8 | 5 | 62% |
| Account takeover | 2 | 2 | 100% |
Horizontal bypass — one user reaching another user’s data at the same privilege level — is serious seven times out of ten, the highest rate of any of them. It is also the one most often missing from a test plan, because it requires two accounts and somebody willing to think about what the second one should not be able to see.
What this class is not
We also record cloud permission findings — over-broad IAM roles, S3 access control, database users with more rights than they need. They belong to a different problem. They are real and worth fixing, and in our data they are mostly not high severity: the “excessive permissions” templates run at 0% high-or-critical across dozens of findings.
Conflating the two inflates the class and points remediation in the wrong direction. A tightened IAM policy does not fix an IDOR. This page is about authorization inside the application — the check that should have happened when a request arrived, and did not.
Why a tool will not hand you these
This follows directly from what the bug is, and it is the reason the class is under-represented in scanner-driven testing.
A cross-site scripting payload looks wrong. There is a string with angle brackets in a field that should hold a name. An SQL injection looks wrong. A path traversal looks wrong. A tool can pattern-match all of them, because the request itself is malformed.
An authorization bypass is a perfectly well-formed request. A valid, authenticated session asks for object 1,041 instead of object 1,040. Every field is the right type. Every value is in range. The signature verifies. Nothing about it is anomalous in any way a scanner can express — and the server answers, because nobody wrote the check that says this user does not own that object.
To find it you have to know what object 1,041 is, who is supposed to see it, and what the roles in this system actually mean. That is not pattern matching. It is reading a system the way its developers do, and then asking what they forgot.
Our data agrees. 309 of the 312 authorization findings came from penetration testing; three came from design, code and architecture review combined. A review of the documentation does not surface these, because the documentation describes the check that was supposed to exist.
How we hunt them
There is no clever tool here. There is a method, and it is mostly bookkeeping done stubbornly.
Enumerate the objects, not the endpoints. A test plan built from a URL list finds vertical bypass and misses horizontal. The unit of work is every kind of thing the application owns — an order, a document, a message, a device — and for each one, every route that reaches it.
Test with two accounts at the same level, not just one of each rank. Most plans include an admin and a user, which finds privilege escalation. Horizontal bypass needs two ordinary users who should be blind to each other, and it is the most dangerous variety we find.
Change the identifier, not the request. Take a working request from user A, substitute an object belonging to user B, change nothing else. If the response differs from a clean denial, that is the finding. Most of the 106 IDORs we have reported were found this way.
Try the object you should not know exists. Sequential identifiers make this trivial; UUIDs make it harder but not impossible, because identifiers leak — in exports, in notification emails, in shared links, in a colleague’s screenshot.
Check the second path to the same data. The API and the web UI often reach the same record through different code, and the check frequently lives on only one of them. So do bulk export, print view, mobile client and reporting endpoints.
Then check whether the fix generalised. More on that below, because it is where most of the remaining risk sits.
They cluster
170 of 694 projects had at least one authorization finding — roughly one in four. Of those, 38% had more than one, and one project had eight.
That shape tells you something about the underlying cause. A single authorization bug is usually a missed check. Several in one application is an architecture where the check is the developer’s responsibility to remember on every route, rather than something the framework enforces by default. The second kind does not get fixed by fixing the findings.
It affects 71 of the 140 organisations we have tested — half of everyone. And it is not improving: the class has held steady at three to four percent of all findings every year since 2021.
The good news, which is real
Having said all that, clients treat these findings the way we would hope, and it shows up in the numbers.
| Authorization findings | All findings | |
|---|---|---|
| Asked us to verify the fix | 40% | 25% |
| Fixed correctly first time | 60% | 42% |
When we report one of these, it comes back for checking far more often than average, and the fix is right first time far more often than average. Of the 105 authorization fixes we have verified, 89 were confirmed fixed. Nobody is ignoring them.
The gap that remains is not carelessness. It is generalisation: seven came back partially fixed, which in this class almost always means the check was added to the object we demonstrated and not to its neighbours. If we showed you an IDOR on invoices, the question at retest is not whether invoices are fixed. It is whether documents, exports and messages are.
What to ask for
Ask how many accounts the test will use, and at what levels. One account per role finds vertical bypass. Two accounts at the same level are what find the horizontal kind, and that is the variety most likely to be serious when it is there.
Ask for object coverage, not endpoint coverage. “We tested every endpoint” and “we tried every object type from every role” are very different statements, and only the second one addresses this class.
Do not accept a scanner report as coverage here. Not because scanners are bad — they are excellent at the classes they can see — but because a well-formed request for someone else’s data is not a class they can see.
Fix the pattern, then ask us to check the pattern. The single most common way an authorization fix fails is being correct and local.
Figures are from our own engagement records, October 2021 to August 2026: 6,643 findings across 890 engagements and 167 organisations. Counts and distributions only — no client is identifiable from any number here. Severity is the severity assigned when the finding was written. See also what 890 security engagements actually find and we checked 1,237 fixes.
