Most applications get authentication right: they ask for a username and a password, perhaps a second factor, and with that they confirm who is coming in. The problem shows up one step later, when the system has to decide what that person can do once inside. That is where access control fails, and that is where the most widespread vulnerability of today's web is concentrated.


OWASP places Broken Access Control at the top of its Top 10, and its most recent measurements are telling: practically every application tested showed some form of deficient access control. This is not an edge case, but a pattern that runs through almost any system with users and permissions.


In this article we'll review what the vulnerability consists of, its most common forms, how it's exploited in both web applications and APIs, and why its impact varies by business. At the end, we'll look at how to mitigate it and what role a role-based access control (RBAC) system plays.

Authentication and authorization are not the same

It's worth starting with a distinction that often gets blurred. Authentication verifies identity, that is, it confirms that someone is who they claim to be. Authorization, in contrast, determines which resources and operations that already-verified identity is allowed to use. They are distinct, sequential controls: first you authenticate, then you authorize.


Access control is the implementation of that authorization, and it's only effective when enforced in trusted server-side code (or equivalent serverless functions), where the attacker cannot alter either the check or the metadata it relies on. Any control that lives only in the browser, such as hiding a button or disabling a field, improves usability, but protects nothing: it's an interface decision, not a security one.


Broken access control, then, occurs when the application doesn't correctly restrict what an authenticated, or even anonymous, user can see or do. Its usual consequences are the disclosure of information to unauthorized parties, the modification or destruction of data, and the execution of business functions beyond the intended scope.

What the vulnerability consists of

Correct access control enforces a policy so that users cannot act outside their intended permissions. The vulnerability arises when that policy fails for one of two reasons: either the application trusts a value the user controls (an identifier, a role embedded in a token, a URL), or it omits the check entirely at some point in the flow.


OWASP maps this category to numerous weaknesses (CWE), among them missing authorization (CWE-862, Missing Authorization), incorrect authorization (CWE-863) and authorization bypass through a user-controlled key (CWE-639). Its cross-cutting nature explains why it tops the ranking: it's not a one-off error, but a family of logic flaws that manifests in many ways.

The forms of broken access control

Privilege escalation: vertical and horizontal

Vertical escalation occurs when a user gains privileges above those they should have (for example, a regular user reaching administrative functions), or when someone acts as an authenticated user without having logged in. Horizontal escalation, on the other hand, keeps the same privilege level but crosses the boundary between peers: a user accessing another equivalent user's data or actions.

Insecure direct object references: IDOR and BOLA

This is probably the most frequent form. In the web world it's known as IDOR (Insecure Direct Object Reference): the application exposes an object's identifier (an account, an invoice, a document) and trusts it without verifying that it belongs to whoever requests it. Simply changing that identifier in the URL or a parameter is enough to access someone else's data.


In the API domain, the same flaw is called BOLA (Broken Object Level Authorization) and tops the OWASP API Security Top 10. It's especially prevalent because an API server usually doesn't keep the client's full state: it relies on the object identifiers arriving in each request, whether sequential integers, UUIDs or generic strings. If the backend doesn't check object ownership on every access, the attacker only has to modify that identifier in the URL, the query string, a header or the request body.


At the weakness level, it corresponds to authorization bypass through a user-controlled key (CWE-639). Using random, unpredictable identifiers, such as GUIDs, raises the difficulty of enumeration, but does not replace the authorization check: it's a defense-in-depth measure, not the control itself.

Function-level authorization: unguarded endpoints and BFLA

A second group of flaws is not in the object, but in the function. It's common to find APIs with missing controls for POST, PUT or DELETE operations, or administrative endpoints reachable through forced browsing, by guessing or inferring a privileged URL. In the API classification this is known as BFLA (Broken Function Level Authorization). The distinction with BOLA is useful: BOLA is accessing an object that isn't yours; BFLA is invoking a function you shouldn't reach at all, such as a regular user running DELETE /admin/users/5.

Metadata manipulation: tokens, cookies and hidden fields

Another route consists of altering the metadata the application uses to decide. This includes replaying or tampering with a JWT token, a cookie or a hidden field to elevate privileges, as well as abusing a poorly implemented JWT invalidation. If the role or the tenant travels in a token the server doesn't validate rigorously, that token becomes the key.

Other variants

Broken access control also covers path traversal, that is, access to files outside the allowed directory (CWE-22); permissive CORS configurations, which allow the API to be consumed from untrusted origins; and mass property assignment (mass assignment), in which the client sends additional fields, such as role=admin, that the server binds to the model without filtering them.

How it's exploited, in web and API

In the web

The most common techniques are direct: parameter manipulation (changing acct=12345 to another value), forced browsing to routes like /admin, altering cookies or JWT tokens, modifying hidden form fields and changing the HTTP verb, for example going from GET to PUT on a resource that should only be read. A recurring detail: when the control lives only in the frontend, a simple curl call bypasses it entirely.

In APIs

APIs today concentrate much of the attack surface. Exploiting BOLA consists of enumerating or substituting object identifiers; exploiting BFLA, of invoking privileged endpoints or switching the HTTP method; and mass assignment, of injecting properties the backend shouldn't accept. Their stateless nature aggravates the problem: by relying on the identifiers arriving in the request, any omitted check is left exposed. A typical flow looks like this:


GET    /api/v1/invoices/1001200 OK   · your invoice
GET    /api/v1/invoices/1002200 OK   · another customer's   ← BOLA
DELETE /api/v1/users/5204      · not an admin         ← BFLA

In all three cases the server responded to an operation it should never have authorized.

The impact depends on the business

A particular trait of this vulnerability is that its severity is not measured in the code, but in what that object or function represents for the business. The same technical defect, an unverified identifier, can be a minor incident or a critical one depending on the domain.

Financial services (fintech and banking)

Here the object is usually an account, a transfer or a statement. An IDOR or a BOLA can translate into access to other people's accounts, unauthorized transfers, massive exposure of personal data and, in the worst case, account takeover. On top of the direct loss come regulatory obligations and the supervisor's scrutiny.

Healthcare

In healthcare, the objects are medical records, test results and prescriptions: sensitive personal data by definition. An authorization flaw exposes third parties' health information, with consequences for people's privacy and for regulatory compliance. In Chile, Law 21.719 treats health data as a specially protected category.

Multi-tenant SaaS

On multi-tenant platforms, the most feared scenario is cross-tenant access: one customer seeing or modifying another's data. Beyond the data itself, it compromises the service's central promise, which is isolation, and with it trust and contracts.

E-commerce and retail

Orders, addresses, coupons and sales data are equally sensitive objects. OWASP documents an illustrative case: an API that exposed per-store sales through a predictable URL pattern, which allowed automated harvesting of thousands of stores' data.


In short, the same finding can be prioritized in very different ways, which is why assessing it requires understanding the business context, not just the technical defect.

How to mitigate broken access control

There is no single countermeasure, but a set of mutually reinforcing practices.

Deny by default

Except for public resources, everything must be denied by default. Access is granted explicitly for specific capabilities, roles or users; never assumed.

Enforce authorization only on the server

Checks must run on the server side, at the gateway or in the serverless function, where the attacker cannot alter them. Client-side control is acceptable for usability, never as a security decision.

Centralize the authorization logic

Authorization must be implemented once and reused throughout the application, through cross-cutting mechanisms such as middleware or filters, rather than repeating the check method by method. A single forgotten control is enough to open a breach. Most frameworks provide building blocks for this, such as Spring Security, Django's middleware, .NET filters or Laravel's middleware.

Verify on every request, every verb and every resource

The control must cover all HTTP methods (GET, POST, PUT, DELETE) and also static resources, such as files in object storage, which often fall outside the policy.

Check object ownership (against IDOR and BOLA)

The model must enforce record ownership rather than allowing operations on any record. There are three complementary strategies: retrieving the object from the authenticated identity, not from an arbitrary client identifier; using per-session indirect references; and performing an explicit authorization check on every access. Verifying authorization in every function that queries records from client input is the golden rule.

Harden tokens and sessions

Stateful session identifiers must be invalidated on the server after logout. Stateless JWTs should be short-lived to narrow the window of abuse, and for longer sessions it's advisable to rely on refresh tokens and OAuth standards to revoke access. Every claim that influences authorization must be validated on the server.

Reduce the surface and observe

It's worth minimizing CORS usage, disabling directory listing and preventing metadata such as .git or backups from remaining accessible under the web root. In parallel, logging authorization failures, alerting on repeated attempts and applying rate limiting reduces the damage from automated abuse.

Test it, and not only with unit tests

Authorization should be covered by unit and integration tests: deny by default, all verbs, safe exit on failures. That said, let's be clear: those tests catch errors early, but do not replace a dedicated assessment. Broken access control is a business-logic flaw, not a signature a scanner recognizes; it requires someone who understands the domain to test every combination of role and object.

RBAC: what a role-based access control system is

Much of the mitigation above relies on a clear authorization model. The most widespread is role-based access control (RBAC).


Its core idea is simple: instead of assigning permissions directly to each user, permissions are assigned to roles and roles to users. A permission is an authorized operation on a resource, for example "read invoices" or "delete users". A role is a set of permissions tied to a function: customer, operator, auditor or administrator. The user inherits the permissions of the roles granted to them.


This model is governed by two principles. Least privilege states that each role should have only the permissions essential to its function. Separation of duties avoids concentrating in a single role capabilities that, combined, would be dangerous. RBAC also admits variants: hierarchical, when roles inherit permissions from others, and constrained, when exclusion rules between roles are imposed.


RBAC's advantage is that it centralizes and makes authorization auditable: permissions stop being scattered and move to an explicit, reviewable scheme. It's worth keeping in mind an important limitation, though: RBAC operates at the function and role level, not the object level. On its own it does not prevent an IDOR or a BOLA, which are object-level flaws; a user with the right role can still access the wrong object if ownership isn't verified. That's why RBAC must be complemented with the ownership check described earlier.


When permission logic exceeds what roles can comfortably express, or when the number of roles grows out of control (a phenomenon known as role explosion), there are more granular models. ABAC (Attribute-Based Access Control) decides based on attributes of the user, the resource and the environment: time, location, device, data ownership. ReBAC (Relationship-Based Access Control) grants access based on the relationship between entities, for example "creator of this document". In practice, many systems combine RBAC for coarse function control with ownership or attribute checks for fine-grained object control.

In summary

Broken access control tops the OWASP Top 10 not because it's sophisticated, but because it's cross-cutting and silent: an alert rarely gives it away, because the system responds normally to an operation that simply shouldn't have been authorized. Mitigating it requires denying by default, centralizing authorization on the server, verifying the ownership of every object and maintaining a well-defined role model.


And because it's a logic flaw, it's found by putting yourself in the place of an attacker who understands the business: systematically testing what each role can see and do over each object. That is precisely the kind of validation a well-run offensive security exercise is designed to find.