GraphNode
Back to OWASP Top 10 hub
OWASP A01

OWASP A01:2021 Broken Access Control: Detection and Prevention

| 11 min read |GraphNode Research

TL;DR

Broken Access Control is the number one risk in the OWASP Top 10 (2021), having moved up from fifth place in the 2017 edition. It covers any failure to enforce that authenticated users can only act on resources they are authorized to touch — including IDOR (Insecure Direct Object Reference), missing function-level authorization, URL tampering, and JWT or CORS misuse. Detection requires layered tooling: SAST flags missing checks and IDOR-shaped data flow patterns at the source level, while DAST and manual review verify runtime authorization enforcement. Prevention is policy-driven: deny by default, enforce server-side, centralize the rules, and apply the principle of least privilege.

Broken access control was the most common security weakness across applications tested in the run-up to the 2021 OWASP Top 10 redesign. The category did not exist as a number-one entry by accident: OWASP's incidence-rate data showed that 94 percent of applications tested were checked for some form of broken access control, and the average failure rate was around 3.81 percent — the highest of any category in the dataset. Combined with high exploitability and high technical impact, the math placed it at the top of the list.

The shift from fifth to first is also a story about how OWASP's methodology evolved. In 2017 the ranking weighted exploitability and impact more heavily, which suited categories like injection. The 2021 methodology rebalanced toward incidence rate — how often the weakness actually appears — and access control rose because it appears almost everywhere authentication is implemented. This guide explains the category in depth: what it covers, the patterns it shows up as, the CWEs it maps to, real-world incidents tied to it, and the layered detection and prevention strategy that actually works.

What Is Broken Access Control

Access control is the enforcement of policy that determines what authenticated users are allowed to do. It is the mechanism that keeps user A from reading user B's invoices, that prevents a regular employee from impersonating an administrator, and that blocks a customer from elevating their own subscription tier through API tampering. Broken access control is any failure of that enforcement — checks that are missing, inconsistent, bypassable, or applied only on the client side.

A point of constant confusion is the difference between authentication and authorization. Authentication answers "who are you?" — login flows, session establishment, JWT issuance, multi-factor verification. Authorization answers "what are you allowed to do?" — role checks, ownership checks, permission matrices, scope validation. A perfectly authenticated user can still trigger a broken-access-control vulnerability if the application fails to verify that their identity is actually permitted for the action they are attempting. The OWASP Top 10 splits these explicitly: A07 covers authentication failures, while A01 covers authorization failures.

The scope is broader than IDOR alone. Broken access control includes any place where the application is supposed to ask "is this user allowed to do this?" and either does not ask, asks but trusts the wrong source for the answer, or asks correctly but fails open. URL guessing past hidden admin pages, modifying a hidden form field to elevate privileges, swapping a session cookie to act as another user, manipulating JWT claims because the server never re-validates them, and sending a forged CORS preflight to access an internal API are all in scope. The category is policy enforcement broadly construed, not a single bug class.

Common Patterns

IDOR (Insecure Direct Object Reference). The canonical pattern. An endpoint at /api/orders/12345 returns the order if the authenticated user owns it — but the developer forgot to verify ownership, so any authenticated user can change 12345 to 12346 and read someone else's order. IDOR shows up wherever a resource identifier flows from user input directly into a database lookup without an ownership clause.

Missing function-level access control. An admin endpoint protected only by a hidden navigation link, or by a client-side role check, with no server-side guard. The classic case is /admin/users/delete reachable by any authenticated session because the developer assumed only admins would know the URL. Forced browsing — guessing or enumerating endpoints — exposes these immediately.

URL and parameter tampering. Modifying values in a request to bypass authorization: changing ?role=user to ?role=admin in a query string the server trusts, or flipping a hidden is_admin form field. The defect is server-side trust of client-supplied state.

JWT manipulation. Tokens that the server signs but does not properly verify on each request, or tokens whose claims (user ID, role, scope) are trusted without re-checking against the database. The notorious "alg: none" JWT bypass falls here, as does the case where a JWT's role claim is trusted indefinitely after issuance even though the user's real-world permissions changed.

CORS misconfiguration. An overly permissive Access-Control-Allow-Origin: * combined with credentials, or origin reflection without an allow-list, lets a malicious site invoke authenticated APIs in the victim's browser. The browser dutifully sends cookies and the server has no defense.

Force browsing. Discovering unlinked endpoints by guessing or wordlist enumeration — /admin, /debug, /internal/health, /api/v1/users. If those endpoints exist and lack a server-side authorization check, they are reachable by anyone who finds them.

Mass assignment. A user-update endpoint that binds the entire request body to the user model, allowing a regular user to set role=admin or email_verified=true by adding the field to the JSON. The defect is over-broad object binding without an explicit allow-list of writable fields.

Real-World Incidents

Capital One (2019). The breach that exposed roughly 100 million records was a chained attack: an SSRF vulnerability in a misconfigured web application firewall let the attacker reach the AWS instance metadata endpoint and retrieve credentials for an over-permissive IAM role, which in turn allowed exfiltration of S3 buckets. The IAM permission scope was broader than the application required — a textbook violation of least privilege and a contributing access-control failure that turned an SSRF into a major breach.

Facebook (2018). A bug in the "View As" feature, which allowed users to see how their profile appeared to others, leaked access tokens for the impersonated viewer. Roughly 50 million accounts had tokens exposed before Facebook detected and reset them. The root cause was a chain of access-control oversights in how tokens were scoped and how the feature interacted with the video uploader, allowing a feature intended for read-only preview to yield reusable session credentials for arbitrary users.

Peloton (2021). An IDOR vulnerability in Peloton's API let any authenticated user query other users' account data — including age, weight, gender, location, and workout history — even when those users had configured their profiles as private. The endpoint accepted a user ID and returned the requested record without verifying that the requesting account was authorized to view it. The fix was simple: enforce the visibility setting server-side instead of trusting the client. The pattern is the textbook IDOR shape.

Relevant CWE Mappings

A01:2021 aggregates 34 underlying CWEs in the official OWASP mapping. The six below are the most common in practice and the ones most teams will recognize from scanner output and finding tickets.

CWETitleWhere It Shows Up
CWE-22Path TraversalUser-controlled filename reaching file system API without normalization
CWE-284Improper Access ControlGeneral catch-all for missing or weak access checks
CWE-285Improper AuthorizationAuthorization logic present but incorrect — wrong role check, wrong scope
CWE-352Cross-Site Request ForgeryState-changing endpoint reachable via cross-site request without anti-CSRF token
CWE-639Authorization Bypass Through User-Controlled KeyThe IDOR pattern: object ID from request goes straight to DB lookup
CWE-862Missing AuthorizationEndpoint or function with no authorization check at all
CWE-863Incorrect AuthorizationCheck exists but evaluates the wrong condition or fails open

CWE-862 (missing authorization) and CWE-863 (incorrect authorization) are the two that scanner output most commonly maps to A01. CWE-639 is the IDOR-shaped subclass that taint-aware static analysis is best positioned to flag. CWE-284 is the umbrella the others descend from when a finding does not fit a more specific entry.

Detection: Where SAST, DAST, and Manual Review Each Help

No single layer fully detects broken access control. The category spans both code-level patterns that show up in source and runtime behavior that only manifests when an actual request reaches a deployed endpoint. A complete program layers three approaches.

SAST is strongest where access control patterns are visible at the source level. A taint-aware static analyzer can flag IDOR-shaped data flows where a user-supplied ID flows directly into a database query without an ownership clause. It can detect endpoints that lack the framework-specific authorization annotation other endpoints in the same controller carry — a missing @PreAuthorize or @require_login decorator. It can flag hardcoded role or admin flags in source, and patterns where a JWT is decoded without the signature being verified. GraphNode SAST traces these patterns across method boundaries with interprocedural data flow analysis. SAST cannot tell you whether the deployed application actually enforces the rule at runtime — it can only tell you whether the rule appears to be present in code.

DAST is the layer that confirms runtime enforcement. With multiple authenticated test accounts and the ability to swap session tokens between requests, a properly configured DAST scanner can detect IDOR by retrieving resource A as user 1, replaying the same request as user 2, and observing whether user 2 receives the data. It catches missing function-level authorization by attempting admin endpoints with non-admin sessions. It catches broken JWT verification by submitting tampered tokens and observing whether the server still accepts them. See DAST explained for the mechanics, and SAST vs DAST for the layered argument.

Manual review remains irreplaceable for the business-logic flaws that no scanner can catch. A multi-step transaction that allows step-skipping. A discount code that can be applied twice through a race condition. A tenant-isolation defect where a multi-tenant SaaS leaks one customer's data to another because the tenant filter was applied in some queries and forgotten in others. These require a human reviewer who understands the application's intended behavior and can construct the abuse case.

Prevention

Deny by default. Authorization should fail closed. Any request that reaches a protected resource without an explicit allow decision should be rejected. Frameworks that require authorization to be opt-in invite the missing-check class of bug; frameworks that require it to be opt-out are safer.

Enforce server-side. Never trust client-supplied claims about identity, role, or permission. The server holds the authoritative copy of the user's role and resource ownership; it must check both on every request. JWT claims should be re-validated against the database when they grant elevated capability, not trusted indefinitely after issuance.

Use a real authorization framework. Hand-rolled role checks scattered across controllers reliably miss endpoints. Centralize authorization in a dedicated module: Spring Security for Java, Django's built-in auth and DRF permissions for Python, Cedar or OPA for policy-as-code regardless of stack. The shared module is the choke point that closes the missing-check gap.

Apply the principle of least privilege. Roles, IAM policies, database accounts, and service-to-service credentials should grant only what is required for the task, not what is convenient. The Capital One incident escalated from an SSRF into a 100-million-record breach precisely because the IAM role attached to the host had broader S3 access than the application needed.

Log and alert on access control failures. Every denied request is a signal — usually noise, occasionally an active attack. Centralize denial logs, set thresholds, and alert when the rate spikes. Rate-limit credential stuffing and authenticated enumeration endpoints. Logging access control failures is also explicitly called out in OWASP A09 Logging and Monitoring; the two categories are linked in practice.

Where GraphNode SAST Fits

GraphNode SAST uses interprocedural taint propagation to trace data from sources (HTTP parameters, request bodies, query strings) through method boundaries into sinks (database queries, file system calls, internal API invocations). For broken access control specifically, this catches the IDOR-class pattern directly: a user-controlled ID flowing into a database lookup without an intervening ownership check is exactly the data flow signature the engine is built to find. It also flags endpoints that lack the framework-specific authorization annotation other endpoints in the same controller carry, hardcoded role and admin flag comparisons, and JWT decoding without signature verification.

What SAST cannot do for this category is verify that the deployed application enforces the rule at runtime. A missing-check finding is a strong signal but needs DAST or manual review to confirm exploitability. SAST also cannot catch business-logic bypasses — the intent of an authorization rule is invisible to a static analyzer; only a human reviewer who understands the application's semantics can identify those. The honest position is that GraphNode SAST is one strong layer in a multi-layer access control program, not the whole program. For the broader picture of how the layers fit together, see the OWASP Top 10 hub.

Frequently Asked Questions

What is broken access control?

Broken access control is the failure to enforce that authenticated users can only access the resources and perform the actions they are authorized for. It covers any place where the application is supposed to ask "is this user allowed to do this?" and either does not ask, asks but trusts the wrong source for the answer, or asks correctly but fails open. It is the number one risk in the OWASP Top 10 (2021), having moved up from fifth place in the 2017 edition.

Is IDOR the same as broken access control?

IDOR (Insecure Direct Object Reference) is a specific subclass of broken access control, not the entire category. IDOR is the pattern where an object identifier flows from user input directly into a resource lookup without verifying that the requesting user is authorized to access that object. Broken access control as an OWASP category covers IDOR plus missing function-level authorization, URL tampering, JWT manipulation, CORS misconfiguration, force browsing, and mass assignment. IDOR maps most cleanly to CWE-639 (Authorization Bypass Through User-Controlled Key).

What is the difference between authentication and authorization?

Authentication answers "who are you?" and is implemented through login flows, session establishment, JWT issuance, and multi-factor verification. Authorization answers "what are you allowed to do?" and is implemented through role checks, ownership checks, permission matrices, and scope validation. A perfectly authenticated user can still trigger a broken-access-control vulnerability if the application fails to verify that their identity is permitted for the action they are attempting. The OWASP Top 10 splits the two: A07 Identification and Authentication Failures covers authentication, A01 Broken Access Control covers authorization.

Can SAST detect broken access control?

SAST detects broken access control partially, not fully. It is well positioned to flag IDOR-shaped data flow patterns where a user-controlled ID reaches a database lookup without an ownership clause, endpoints missing framework-specific authorization annotations, hardcoded role and admin flag comparisons, and JWT decoding without signature verification. It cannot verify that the deployed application enforces the rule at runtime, and it cannot catch business-logic bypasses where the intent of an authorization rule is invisible to a static analyzer. Complete coverage requires SAST plus DAST plus manual review.

Why did A01 move from #5 to #1 between 2017 and 2021?

Two reasons. First, OWASP's incidence-rate data showed broken access control was the most commonly tested-for and most commonly failing weakness across the dataset — 94 percent of applications were tested for it and roughly 3.81 percent of those tests found at least one instance, the highest rate of any category. Second, OWASP rebalanced the 2021 ranking methodology to weight incidence rate more heavily than exploitability and impact. The combination put broken access control at the top of the list, where it remains the canonical reference for authorization-class risks in 2026.

Detect Missing Authorization Checks in Source Code

GraphNode SAST traces user-controlled IDs into database lookups, flags endpoints missing authorization annotations, and surfaces IDOR-shaped patterns across 13+ languages with deep interprocedural data flow analysis.

Request Demo