There is a category of vulnerability that almost every automated security tool is structurally incapable of finding, and it happens to be the category that causes the most damage when it reaches production.
Broken access control has topped the OWASP Top 10 since 2021. Anyone who has read a breach post-mortem over the last few years will recognize the pattern: a user identifier in a URL, an object reference in a JSON body, or a tenant ID in a request header—with no server-side verification that the requesting session is actually authorized to access the requested resource.
Simply change the identifier, and someone else’s data becomes accessible.
The interesting question is not why these vulnerabilities exist. Developers introduce them because authorization is application-specific business logic that no framework can completely abstract away.
The more important question is why so many of these flaws survive automated testing and ultimately reach production.
The Visibility Problem
A conventional security scanner operating without credentials sees only a login page and a handful of publicly accessible routes. Everything that matters lives behind authentication.
Even when a scanner is provided with credentials, it typically receives only one authenticated account. That allows it to crawl authenticated pages, but it has no second identity for comparison.
Without another user account, the scanner cannot determine whether User A can improperly access User B’s information, because User B simply does not exist within the test.
This is the fundamental reason access control vulnerabilities frequently escape automated testing.
The limitation is not a weak signature database.
It is the absence of a meaningful test oracle.
A scanner receives an HTTP 200 OK response containing a valid JSON payload and records the request as successful. It has no way to determine whether the invoice, customer record, or project returned actually belongs to another organization.
What the Data Shows
Aggregated testing data illustrates the size of this visibility gap quite clearly.
Across 47,291 exploitation-validated findings collected from 3,847 web applications and APIs tested between October 2025 and March 2026, scans performed using authenticated sessions identified 3.4 times more vulnerabilities, on average, than scans of the same applications performed without authentication.
Among those findings:
- Broken access control was the most common vulnerability category.
- It appeared in 42% of all tested applications.
- Nearly every IDOR (Insecure Direct Object Reference), privilege escalation issue, and broken business logic vulnerability remained completely invisible during unauthenticated scanning.
The complete dataset is publicly available from Penetrify under the CC BY 4.0 license.
That 3.4× difference deserves attention.
It means an unauthenticated scan is not simply producing an incomplete picture of the attack surface—it is systematically missing the classes of vulnerabilities that require reasoning about identity and ownership.
Why APIs Made This Worse
The industry’s shift toward API-first architectures dramatically expanded the number of places where authorization can fail.
Traditional server-rendered applications typically enforced authorization at a relatively small number of controller entry points.
Modern applications built with frameworks such as React, Vue, or Angular interact with REST or GraphQL APIs that expose individual objects through many separate endpoints.
Every one of those endpoints requires its own authorization checks.
Framework scaffolding and code generators have accelerated development but introduced another challenge.
Generating CRUD endpoints from a database schema produces routes that are:
- Consistent
- Well documented
- Strongly typed
- Structurally uniform
Unfortunately, they also tend to share the same authorization posture, which is often limited to verifying that a request contains a valid authentication token.
Authentication is generally handled by middleware.
Authorization remains the responsibility of the application logic, where developers must verify ownership, tenant membership, or role permissions for every individual request.
Forgetting to implement that verification in even one handler can expose sensitive data.
GraphQL Expands the Authorization Surface
GraphQL introduces an additional layer of complexity.
Instead of many endpoints, GraphQL exposes a single endpoint capable of accepting almost unlimited query structures.
This changes the authorization surface from a collection of routes into the schema itself.
Authorization must now be enforced within individual resolvers at both:
- Object level
- Field level
A nested relationship that skips one authorization check can unintentionally expose information that the top-level query was never intended to reveal.
As GraphQL schemas continue to grow, maintaining consistent authorization logic across every resolver becomes increasingly difficult, making broken access control even easier to introduce—and harder to detect.
What Actually Tests for This
Finding access control vulnerabilities requires security tooling to do things that a traditional crawler simply cannot.
Hold Multiple Sessions Simultaneously
At a minimum, testing should involve:
- Two user accounts with the same privilege level but belonging to different tenants.
- One lower-privileged account for privilege escalation testing.
Without multiple identities, there is no meaningful way to verify whether one user can improperly access another user’s data.
Understand Object Ownership
The testing process must build a map of which resources belong to which authenticated session.
It then needs to replay requests using another user’s credentials to determine whether authorization checks are correctly enforced.
That ownership mapping is where much of the real work happens.
Distinguish Legitimate Responses from Authorization Failures
A successful HTTP response does not automatically indicate a vulnerability.
For example:
- A 200 OK response containing an empty dataset may represent correct authorization behavior.
- A 200 OK response containing another tenant’s records is a critical security issue.
The only reliable way to distinguish between the two is to compare response payloads while ignoring naturally changing values such as timestamps or request identifiers.
Chain Related Findings Together
Some vulnerabilities appear harmless when viewed individually.
For example:
- A verbose error message exposing an internal object identifier may be considered low severity.
- An endpoint accepting that identifier without ownership validation may also appear relatively minor on its own.
Together, however, those two issues form a complete data exposure path.
Traditional scanners typically report them as separate findings.
Human penetration testers naturally connect with them because they build a mental model of the application during testing.
Modern AI penetration testing approaches attempt to bridge that gap by allowing intelligent agents to reason about application behavior, chain multiple observations together, and actively attempt exploitation rather than simply matching signatures.
That makes multi-step attack paths significantly more practical to discover automatically.
Practical Implications
If you are responsible for application security, several practical lessons follow.
Verify That Authenticated Scans Are Actually Authenticated
Many organizations believe they are running authenticated scans.
In reality, expired sessions often cause scanners to spend months crawling nothing more than the application’s login page.
Do not rely solely on scanner configuration.
Verify authentication by reviewing the application’s request logs.
Maintain Multiple Test Accounts
Create at least two active test accounts in separate tenants and keep them available.
Although this sounds like simple administrative work, it is one of the highest-value improvements most teams can make to their testing process.
Treat Access Control as a Pattern, Not an Individual Bug
When an IDOR or other authorization flaw is discovered on one endpoint, avoid fixing only that single location.
Instead, search the codebase for similar authorization patterns.
The same implementation mistake frequently appears in multiple handlers.
Correcting the underlying pattern is far more effective than fixing isolated instances.
Test at Deployment Frequency
Modern applications change continuously.
An authorization check accidentally removed during a refactor is inexpensive to catch immediately after deployment and considerably more expensive to discover months later.
Integrating continuous security monitoring into the development pipeline is less about sophisticated tooling and more about reducing the time between introducing a vulnerability and discovering it.
The Wider Point
Over the past two decades, the security industry has become exceptionally good at identifying vulnerability classes that are visible from outside the application’s trust boundary.
These include:
- Injection flaws
- Security misconfigurations
- Outdated software components
- Missing security headers
For organizations running even basic security tooling, these have become relatively well-understood problems.
The vulnerabilities that remain are fundamentally different.
They require testing from inside the application while acting as an authenticated user and asking a much more important question:
Should this identity actually be allowed to perform this action?
That represents an entirely different testing mindset.
Many organizations have not yet made that transition.
The 3.4× difference between authenticated and unauthenticated testing is not simply another statistic.
It reflects how much of today’s real attack surface still goes unexamined when testing stops at the login page.
Author Bio
Viktor Bulanek is the founder of Penetrify, an autonomous penetration testing platform based in Brno, Czech Republic. The dataset referenced in this article is publicly available under the CC BY 4.0 license at penetrify.cloud/en/stats.