Back to blog
Web Security4.07.2026

Broken Access Control (OWASP A01:2021): The #1 Web Application Risk

Broken Access Control ranks as A01:2021 in the OWASP Top 10 — the number one risk to web applications. It moved up from fifth place in 2017 because it is both pervasive and high-impact: OWASP found that 94% of applications tested had some form of broken access control, and it accounts for more Common Weakness Enumeration (CWE) occurrences than any other category. Access control is where authentication ends and authorization begins: authentication proves who you are; access control decides what you are allowed to do. When those decisions are missing, incomplete, or enforced only in the browser, an attacker can read or modify data and functions that should be off-limits.

What Access Control Actually Enforces

A correct access-control model answers a simple question on every request: is this specific subject allowed to perform this action on this specific object? Failures happen when the answer is assumed rather than checked. The two classic failure directions are horizontal and vertical privilege escalation.

  • Horizontal escalation — a user accesses resources belonging to another user at the same privilege level. Example: user A opens /api/orders/1043, changes the ID to 1044, and reads user B's order. Same role, different owner.
  • Vertical escalation — a user gains capabilities of a higher privilege level. Example: a standard user calls POST /api/admin/users to create accounts, or flips a hidden role=admin field. Different role entirely.

The Common Patterns

IDOR / BOLA (Insecure Direct Object Reference / Broken Object Level Authorization). This is the flagship horizontal-escalation bug and, as BOLA, ranks API1:2023 in the OWASP API Security Top 10. The application exposes a direct reference to an object — a database ID, a filename, a UUID — and trusts the client to supply only references it owns. Because the server never re-verifies ownership, tampering with the identifier yields someone else's data. Predictable, sequential IDs (?invoice=8801) make enumeration trivial, but note: replacing sequential IDs with UUIDs is obfuscation, not a control. The fix is a server-side ownership check, not an unguessable identifier.

Missing function-level authorization. Administrative or privileged functions are protected only by not showing the link in the UI. The endpoint itself performs no role check. An attacker who knows or guesses /admin/delete-user — often trivially discoverable in JavaScript bundles or through fuzzing — invokes it directly. This maps to CWE-285 (Improper Authorization) and CWE-862 (Missing Authorization).

Forced browsing. An attacker requests URLs, parameters, or HTTP methods that are not linked anywhere but are still served. Requesting /reports/2026/q1-confidential.pdf directly, or sending PUT/DELETE to an endpoint that only guards GET, bypasses controls that assumed users would only follow provided links. Metadata endpoints, backup files, and debug routes are frequent finds.

Other frequent variants: allowing CORS misconfiguration to expose APIs to untrusted origins; JWTs whose claims (like role or sub) are trusted without validating the signature; and mass-assignment, where a request body sets fields (isAdmin, accountBalance) the client should never control.

Real-World Impact

Access-control failures are not theoretical. The 2018 USPS Informed Delivery flaw let any logged-in user query the account data of ~60 million users through an unauthenticated API parameter — a textbook BOLA. The 2019 First American Financial exposure leaked 885 million mortgage records through sequential document IDs requiring no authentication whatsoever. Under CVSS v3.1, such issues often score 8.0+ (High) — for example a network-exploitable IDOR leaking confidential data scores CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N = 6.5, rising toward 8–9 when integrity or admin functions are also reachable.

How to Test for It

  • Two-account differential testing. Create two users (and one admin). Capture a request from account A, then replay it with account B's session cookie/token. If B receives A's data, you have a horizontal-access flaw. Tools like Burp Suite's Autorize or Auth Analyzer automate this side-by-side comparison.
  • Parameter tampering. Increment, decrement, and fuzz every object identifier — IDs, UUIDs, filenames, account numbers — including those in JSON bodies and JWT claims.
  • Function-level probing. Enumerate admin and privileged endpoints from JS source maps and directory brute-forcing, then invoke them with a low-privilege session.
  • Method and force-browse testing. Try alternate HTTP verbs and directly request unlinked resources. Align coverage with OWASP WSTG-ATHZ (Authorization Testing) test cases.

How to Prevent It

  • Deny by default. Every resource is private unless a rule explicitly grants access. Public endpoints are the exception you whitelist, never the default you forget to lock.
  • Enforce on the server, always. Client-side checks are UX, not security. The authoritative decision must run on the server, on every request, for every object.
  • Check ownership, not just role. Confirm the authenticated subject actually owns or is entitled to this record. Derive the user identity from the session/token — never from a client-supplied user_id parameter.
  • Centralize the logic. Route all decisions through a single authorization component or middleware rather than scattering ad-hoc if checks. This aligns with NIST SP 800-53 AC-3 (Access Enforcement) and the principle of least privilege (AC-6).
  • Use indirect references and rate-limit. Prefer per-session mapped references, log access-control failures, and alert on repeated denials to catch enumeration.
  • Test continuously. Add automated authorization tests to CI so a new endpoint cannot ship without an ownership check.

Access control is design work, not a filter you bolt on at the end. Model roles and object ownership explicitly, enforce every decision server-side with deny-by-default, and verify it with two-account testing on every release. That discipline is what turns the OWASP #1 risk into a non-event.

OWASPaccess-controlIDORBOLAauthorization