Imagine you’re visiting a secure, high-tech corporate office building. You walk up to the reception desk, prove who you are with your ID, and receive a visitor badge. This badge grants you access to the main lobby and the visitor lounge.
Now, imagine if you could take the elevator to the penthouse, walk straight into the CEO’s office, and read confidential files on their desk simply because the elevator buttons weren’t restricted and the office door was left unlocked.
In the digital world, this failure is known as Broken Access Control.
Access control is the mechanism that ensures users cannot act outside of their intended permissions. When access control is broken, users can access resources or perform actions that they should not be authorized to access.
In the latest OWASP Top 10, Broken Access Control holds the infamous #1 spot as the most common and serious web application security risk. Let’s dive deep into how it works, explore real-world examples, and learn the rules to mitigate it.
Authentication vs. Authorization
Before we look at the vulnerabilities, we must clarify a common point of confusion:
- Authentication (AuthN): “Who are you?” This is the process of verifying a user’s identity (e.g., logging in with a username and password, or using multi-factor authentication).
- Authorization (AuthZ): “What are you allowed to do?” This is the process of verifying if an authenticated user has the permission to access a specific resource or execute a specific action.
Broken Access Control is fundamentally an authorization failure. The user has successfully logged in (Authentication works), but the system fails to restrict their actions based on their identity or role (Authorization fails).
The Core Types of Access Control
Understanding how access control is structured helps us identify where it breaks. Modern applications typically use a mix of these models:
- Discretionary Access Control (DAC): The owner of a resource decides who gets access. (e.g., sharing a Google Doc with a colleague).
- Mandatory Access Control (MAC): A central authority regulates access based on security levels. (e.g., military clearance levels).
- Role-Based Access Control (RBAC): Access is assigned to specific “roles” (e.g., Admin, Editor, Viewer), and users are assigned to these roles.
- Attribute-Based Access Control (ABAC): A highly flexible model where access is granted based on attributes (e.g., “Allow access only if the user is in the HR department AND the current time is between 9 AM and 5 PM”).
4 Common Broken Access Control Vulnerabilities
Let’s look at the most frequent access control failures that developers introduce, accompanied by vulnerable and secure code examples.
1. Insecure Direct Object References (IDOR)
An IDOR occurs when an application exposes a direct reference to an internal database object (like a database ID or file name) in a URL or API request, allowing an attacker to manipulate that reference to access another user’s data.
The Vulnerable Code (Node.js / Express)
Consider an endpoint that retrieves invoices for a logged-in user:
| |
- The Exploit: An attacker logs in as User A (Invoice ID
1001). By changing the request to/api/invoices/1002, they can view User B’s invoice because the code only retrieves the invoice by ID without checking ownership.
The Mitigated Code
To fix IDOR, the application must verify that the authenticated user has permission to access the requested resource:
| |
2. Privilege Escalation
Privilege escalation occurs when a user obtains access to resources or functions that are reserved for users with higher privileges (Vertical) or similar privileges (Horizontal).
- Vertical Privilege Escalation: A regular user accesses administrative functions (e.g., changing their role to “Admin”).
- Horizontal Privilege Escalation: A user accesses another user’s private account or resources (essentially IDOR).
The Vulnerable Code
Many applications trust the client-side user interface to hide admin buttons, but fail to protect the underlying API endpoint on the server:
| |
- The Exploit: Even if the “Delete User” button is hidden in the React/Angular UI for non-admin users, an attacker can capture the API request using a tool like Postman or Burp Suite and send the request manually.
The Mitigated Code
Implement server-side middleware to enforce role checks:
| |
3. Path Traversal (Directory Traversal)
Path traversal occurs when an application accepts file paths from user input without validation, allowing attackers to navigate up the directory structure (using ../) and read sensitive files from the server’s local file system.
The Vulnerable Code
Consider a profile picture endpoint that reads a file name from the query parameters:
| |
- The Exploit: An attacker sends a request like
/view-image?file=../../../../etc/passwdto bypass thepublic/imagesdirectory constraint and read the system’s password file.
The Mitigated Code
Sanitize user input and verify that the resolved path stays within the intended directory:
| |
4. Missing Method-Level Access Control
APIs often offer multiple HTTP methods (GET, POST, PUT, DELETE) for a single resource. A common mistake is securing only some of these methods while leaving others unprotected.
For example, a developer might write a route controller that permits access to GET /api/reports only for authenticated users, but forgets to protect POST /api/reports or DELETE /api/reports, allowing unauthorized users to create or delete reports.
Crucial Mitigation Rules
Securing access control requires a structured approach across your entire software development lifecycle. Follow these golden rules:
1. Enforce “Deny by Default” (Default Deny)
When designing your routing and API structure, explicitly deny access to all endpoints by default. Only expose endpoints to the public or to specific roles through active configuration.
2. Never Trust the Client
Never rely on client-side routing, hidden UI elements, or obfuscation to protect sensitive features. Every request arriving at the server must be assumed hostile and verified.
3. Use Indirect References (UUIDs)
Instead of auto-incrementing integers (like 1, 2, 3) for resource IDs, use cryptographically secure identifiers like UUIDs (Universally Unique Identifiers).
- Predictable:
https://example.com/api/users/42(Easy to guess user43) - Unpredictable:
https://example.com/api/users/d883b632-4752-4a57-bc09-5a507871b6ba(Note: UUIDs do not replace authorization checks, but they prevent bulk-guessing and scraping attacks).
4. Write Unit and Integration Tests for Authorization
Make access control test-driven. Write tests that specifically try to access admin and user resources using guest or lower-privileged accounts.
| |
5. Log and Monitor Access Violations
Log all access control failures on the server side. If a user tries to access a resource they do not own or a privilege they do not have, log this event as a warning. Frequent authorization failures from a single IP address can indicate an active scanner or attacker attempting to exploit IDOR vulnerabilities.
Summary
Broken Access Control is a severe but highly preventable vulnerability. By adopting a deny-by-default stance, implementing strict server-side checks for every single API method and parameter, and avoiding client-side security assumptions, you can protect your application from unauthorized access and safeguard your users’ sensitive data.
Secure by design, build with caution!
