OWASP Broken Access Control

Broken Access Control is A01 — the number one risk in the OWASP Top 10. It appeared in 94% of applications tested in the 2021 data set. Authentication asks "who are you?". Access control asks "what are you allowed to do?". When access control breaks down, logged-in users reach data and functions they should never be able to touch.

The Office Building Analogy

A company issues ID badges for the lobby, the cafeteria, and the public meeting rooms. The server room requires a special badge. Broken access control is like a door that checks whether you have any badge — not whether your badge grants server room access. Every employee, from intern to CEO, can walk into the server room.

Types of Broken Access Control

Insecure Direct Object Reference (IDOR)

This is the most common form. The application exposes a direct reference to a resource (like a database ID) in the URL, and anyone can change that number to access someone else's data.

  Alice is logged in and views her order:
  https://shop.com/orders?id=1042

  She changes the URL to:
  https://shop.com/orders?id=1041

  The server returns Bob's order — including his name, address, and items.
  The server never checked whether Alice owns order 1041.

Privilege Escalation (Vertical)

A regular user accesses admin-only functions by navigating directly to admin URLs.

  Regular user URL:    https://app.com/user/settings
  Admin URL:           https://app.com/admin/users/delete

  Application only hides the link — it never checks permissions server-side.
  Any user who types the admin URL gets full access.

Privilege Escalation (Horizontal)

A user accesses another user's resources at the same privilege level. This is what happened in the IDOR example above.

Forced Browsing

The application does not link to certain pages, but those pages exist and are accessible if you guess or know the URL.

  Unlinked but accessible:
  https://payroll.company.com/reports/all-salaries-2024.xlsx

Missing Function-Level Access Control

API endpoints that perform sensitive actions (delete user, export data, change role) are never protected by permission checks. Attackers discover them through trial and error or by reading JavaScript files.

Full Attack Scenario: IDOR on a Healthcare App

  1. Attacker creates an account and books appointment ID 5500

  2. Attacker visits:
     https://clinic.com/appointment?id=5500
     -- Sees their own data

  3. Attacker tries IDs 5499, 5498, 5497 ...
     -- Sees other patients' names, dates of birth, diagnoses

  4. Attacker writes a script to pull IDs 1 through 6000
     -- Downloads 6,000 patient records in minutes

  5. Data sold or used for fraud

How to Prevent Broken Access Control

1. Enforce Access Control Server-Side, Every Request

Never trust the client to enforce access. Every API call and every page load must verify on the server that the currently logged-in user has permission to access the requested resource.

  Correct check:
  GET /orders?id=1041
     |
     Server checks: Does the session user own order 1041?
     If NO  --> Return 403 Forbidden
     If YES --> Return order data

2. Use Indirect Object References

Instead of exposing the database ID in the URL, map it to a random, unpredictable token per user session.

  Database ID 1041 maps to token "x9f2kz" for Alice's session
  Alice's URL: /orders/x9f2kz
  Token is valid only for Alice — Bob cannot use it

3. Deny by Default

Start with no permissions and grant them explicitly. If a check is missing, the user gets no access. This is safer than allowing everything by default and trying to block individual actions.

4. Log Access Control Failures

When a user gets a 403 response, log it. A burst of 403s from one account is a strong signal of an IDOR enumeration attack.

5. Test Access Control in Every Security Review

Use different test accounts at different privilege levels. Confirm that low-privilege users cannot reach high-privilege functions, and that users cannot access each other's resources.

Quick Prevention Checklist

  [✓] Server-side permission check on every request
  [✓] Deny by default for all protected resources
  [✓] No sequential predictable IDs exposed in URLs
  [✓] Log and alert on access control failures
  [✓] Test with multiple user roles during development

Key Takeaway

Access control must be enforced on the server for every single request. Hiding links or relying on the client provides zero security. Deny by default and verify permissions explicitly for every action.

Leave a Comment