For two consecutive releases, the Open Worldwide Application Security Project (OWASP) has identified Broken Access Control (BAC) as the top security risk for web applications. Similarly, Broken Object Level Authorization (BOLA), which is still an access control issue, ranks first among API vulnerabilities. Why does this problem persist? What are the leading causes that hinder effective solutions? What are the best practices to address this issue?
The Persistence of Logical Vulnerabilities
In the 2021 edition, Broken Access Control ranked first in the OWASP Top 10 web application security risk list. It is still first in the ranking just released for 2025:

Technical and logical vulnerabilities
To understand why authorization failures persist, we must first distinguish between technical and logical vulnerabilities.
Technical vulnerabilities, such as SQL Injection (SQLi) or Cross-Site Scripting (XSS), follow recognizable syntax patterns. Modern Static Application Security Testing (SAST) tools are good at identifying these because they can parse code structure to find dangerous concatenations or buffer overflows.
In contrast, Broken Access Control and Broken Object Level Authorization are logic flaws. They represent a semantic gap where automated tools can understand the code's structure but lack the context to understand the business intent.
Broken Access Control and Broken Object Level Authorization
But what are BAC and BOLA? Let’s break them down briefly before exploring why they are so hard to fix.
Broken Access Control (BAC) is the overarching category describing any failure in the mechanism that governs what an authenticated user is permitted to do. It includes vertical privilege escalation (e.g., accessing admin functions) and metadata manipulation.
Broken Object Level Authorization (BOLA), formerly known as Insecure Direct Object Reference (IDOR), occurs when an application fails to validate that a user has rights to a specific data object (e.g., accessing /api/orders/12345 without owning that order). Like BAC, BOLA still persists as the most critical risk for APIs from 2019 to 2023.
BAC, BOLA, and scanning tools
SAST tools lack the runtime context to know if a specific resource should be restricted to the current user. They lack access to the access control matrix or the graph of relationships defined in the database, which is why they cannot guess the business logic.
On the other hand, Dynamic Application Security Testing (DAST) tools often view an HTTP 200 OK response containing user's data as a successful request, not a data breach, because they do not understand the underlying business rules.
To take a literary example, SAST and DAST tools are like the dog in Conan Doyle's novel The Adventure of Silver Blaze. Sherlock Holmes solves a crime because of the “curious incident of the dog in the night-time”: the dog didn't bark when the horse Silver Blaze was stolen. This meant the intruder was someone the dog knew: a trusted insider or a person with legitimate access.
Similarly, the reason automated security tools fail to catch BAC is the same reason the dog didn't bark: the request looks perfectly normal to the system. Just as the dog lacked the context to recognize that the action was suspicious, these tools also lack the business context needed to determine whether an action is legitimate or not.
Why Is Fixing Broken Access Control So Complex?
The persistence of these vulnerabilities could suggest a psychological reason: the industry's reliance on automated scanning creates a false sense of security. But this would be too simplistic an explanation. Let’s analyze a few possible reasons that make Broken Access Control vulnerabilities so persistent.
The complexity of distributed authorization
The transition from monolithic applications to complex, hybrid-cloud ecosystems has dissolved the traditional security perimeter. In modern microservices architectures, authorization logic is often decentralized. A developer working on an "Inventory Service" may implement different logic than someone working on a "User Service," leading to inconsistent enforcement and invisible gaps.
Furthermore, APIs are stateless by design. Unlike traditional sessions where the server remembers context, every single API request must be re-authorized for every object access. This is a requirement frequently missed under development pressure.
Misconceptions between authentication and authorization
A fundamental driver of BOLA is the confusion between authentication and authorization. Developers often mistake the presence of a valid JSON Web Token (JWT) or session cookie as proof of permission to access any resource. In a BOLA exploit, the attacker's identity is verified (authentication passes), but the server fails to check if that identity owns the requested resource (authorization fails).
Do you recall the dog that didn’t bark in Doyle’s novel? In this case, the server acts like that dog: it recognizes the user but lacks the context to determine whether they should access that resource.
Identity sprawl and Non-Human Identities (NHI)
The modern infrastructure landscape is a "fragmented landscape of identity" spanning human users, service accounts, and ephemeral workloads. Non-Human Identities (NHIs) are now far more numerous than human users, and the situation tends to get even more complicated with the spread of AI agents.
These identities often suffer from privilege creep, where they are over-permissioned to avoid breaking production flows. If a single service account is compromised, the "blast radius" is immense because permissions are often added but rarely removed.
Ad-hoc evolution of controls
Unlike authentication, which can be handled by standardized providers such as Okta or Auth0, authorization is deeply intertwined with business logic. It is rarely deliberately designed and instead evolves ad-hoc.
Imagine a document management system. A simple check (if user is owner) becomes a "spaghetti" of if/else statements as features like "Teams," "Shared Folders," or "Admin Overrides" are added, making the code hard to read and test.
Growing an authorization system without a blueprint risks becoming a Winchester Mystery House with inconsistent controls, sometimes useless or curious, and often challenging to understand.
Guidelines for Better Access Control
Achieving robust authorization requires moving away from implicit, ad-hoc checks toward explicit, architected patterns. Here are some suggestions to improve the posture of our applications towards access control vulnerabilities.
Centralize your authorization code
Don't scatter access control logic across the myriad of microservices or components that make up your system.
To effectively manage access control in distributed environments, adopt the Policy Decision Point (PDP) and Policy Enforcement Point (PEP) pattern. By decoupling the decision-making logic (PDP) from the enforcement logic (PEP), you ensure that authorization is not just a series of if/else statements scattered across services, but a centralized, auditable process.
In this architecture, the PEP resides within your microservices or API Gateway, responsible for intercepting requests and enforcing the final decision. The PDP acts as the centralized engine (such as OpenFGA or OPA) that evaluates policies against the provided runtime context and identity attributes to return an allow or deny verdict.
Implement Policy as Code (PaC)
The industry is moving toward Policy as Code to decouple authorization logic from the application code. Using tools such as Auth0 FGA, OpenFGA, or Open Policy Agent (OPA), developers can offload decisions to a dedicated engine. This approach allows security teams to update policies without redeploying the entire application.
Also, policies can be managed by a version control system like Git, which enables navigation of the history of changes and revert to a previous valid state in case something is not working as expected.
Finally, PaC potentially enables automated testing that helps mitigate any vulnerabilities that may emerge following policy changes. For example, Auth0 FGA allows you to set up automatic tests that you can integrate in your continuous development and integration pipeline.
Use fine-grained authorization
It's important to not only check if a user has the appropriate role to access a resource but also to ensure that they possess the necessary permissions to perform the requested action on that specific resource. Implementing a fine-grained authorization system enables better management of access control and helps prevent vulnerabilities like BOLA, as discussed in this article.
Implement scoped database access
Developers must move away from generic queries that rely solely on ID parameters. Instead of a vulnerable pattern like Order.findById(params.id), use a pattern that implicitly enforces ownership. By scoping the query to the authenticated user's context (e.g., currentUser.Orders.findById(params.id)), the data access layer enforces authorization even if a manual check is forgotten in the controller.
Apply schema-based validation
To prevent Mass Assignment attacks, where attackers attempt to overwrite sensitive fields like role or company_id, developers should strictly enforce input schemas using tools like JSON Schema or Zod. If an API endpoint expects only a username, the schema should automatically reject any request containing an id or role.
Let’s Build a More Secure Future
Broken Access Control remains the top threat in 2026 because it is a failure of manageability in increasingly complex systems. Because automated scanners struggle to interpret business intent, the defense against BAC and BOLA must be designed into the architecture. By adopting Fine-Grained Authorization (FGA), implementing deny by default policies, and scoping data access to the user context, you can build resilient systems that effectively close the gap between identity and permissions.
For more information on implementing these patterns, refer to the OWASP Top 10:2025 guide and the Auth0 FGA Documentation.
About the author
Andrea Chiarelli
Principal Developer Advocate
I have over 20 years of experience as a software engineer and technical author. Throughout my career, I've used several programming languages and technologies for the projects I was involved in, ranging from C# to JavaScript, ASP.NET to Node.js, Angular to React, SOAP to REST APIs, etc.
In the last few years, I've been focusing on simplifying the developer experience with Identity and related topics, especially in the .NET ecosystem.
