Broken Access Control
Broken Access Control is the most critical web application security risk, allowing attackers to bypass authorization and act as other users or access unauthorized functionality and data. It occurs when applications fail to properly enforce what authenticated users are allowed to do.
What Is Broken Access Control?
Broken Access Control refers to vulnerabilities that allow users to act outside of their intended permissions. Access control enforces policy such that users cannot act beyond their intended privileges. When these controls fail or are improperly implemented, attackers can exploit these weaknesses to access unauthorized functionality or data, modify other users' information, or elevate their privileges to administrator level.
The vulnerability arises when applications rely solely on client-side checks, security through obscurity (hiding URLs or IDs), or when server-side authorization checks are missing, incomplete, or bypassable. Modern applications often have complex permission models involving roles, groups, and resource ownership, making access control implementation challenging and error-prone.
According to OWASP, Broken Access Control moved up from the fifth position to become the number one vulnerability in the 2021 Top 10, with 94% of applications tested having some form of broken access control. The average incidence rate was 3.81%, with over 318,000 occurrences in the dataset, making it the most prevalent and dangerous category of web application vulnerabilities.
How It Works
A legitimate user logs into the application with valid credentials. The application verifies their identity and creates a session, but authentication alone does not determine what the user should be allowed to access or modify.
The application uses simple, sequential, or guessable identifiers in URLs or API endpoints, such as /api/users/1234/profile or /admin/dashboard. These endpoints may be hidden from the UI but remain accessible if discovered.
The attacker manipulates URL parameters, form fields, or API requests to access resources they should not have permission to view or modify. For example, changing userId=5678 in a request to access another user's data, or navigating directly to /admin paths.
The server-side code checks whether the user is authenticated but fails to verify whether the authenticated user has permission to access the specific resource or perform the requested action. The application trusts that the request is legitimate because the user has a valid session.
The application processes the request and returns sensitive data or performs unauthorized actions. The attacker successfully accesses another user's profile, views confidential documents, modifies data they don't own, or executes administrative functions without proper privileges.
Vulnerable Code Example
@RestController
@RequestMapping("/api/users")
public class UserController {
@Autowired
private UserRepository userRepository;
@GetMapping("/{id}/profile")
public ResponseEntity<?> getUserProfile(@PathVariable Long id) {
// VULNERABLE: Only checks if user is authenticated,
// not if they have permission to access this specific profile
User user = userRepository.findById(id)
.orElseThrow(() -> new ResourceNotFoundException("User not found"));
return ResponseEntity.ok(Map.of(
"id", user.getId(),
"email", user.getEmail(),
"ssn", user.getSsn(),
"salary", user.getSalary()
));
}
}
// Any authenticated user can access ANY other user's profile:
// GET /api/users/1/profile (returns user 1's data)
// GET /api/users/2/profile (returns user 2's data)
// GET /api/users/999/profile (returns user 999's data)
//
// An attacker can enumerate all user IDs and extract
// sensitive information (SSN, salary) from all users.Secure Code Example
@RestController
@RequestMapping("/api/users")
public class UserController {
@Autowired
private UserRepository userRepository;
@GetMapping("/{id}/profile")
@PreAuthorize("hasRole('ADMIN') or #id == authentication.principal.id")
public ResponseEntity<?> getUserProfile(
@PathVariable Long id,
@AuthenticationPrincipal UserDetails currentUser
) {
// SECURE: Verify the requesting user has permission
// to access this specific profile
User user = userRepository.findById(id)
.orElseThrow(() -> new ResourceNotFoundException("User not found"));
// Additional manual check (defense in depth)
if (!currentUser.getUsername().equals(user.getEmail())
&& !currentUser.getAuthorities().contains(new SimpleGrantedAuthority("ROLE_ADMIN"))) {
throw new AccessDeniedException("You do not have permission to access this profile");
}
return ResponseEntity.ok(Map.of(
"id", user.getId(),
"email", user.getEmail(),
"ssn", user.getSsn(),
"salary", user.getSalary()
));
}
}
// With proper authorization:
// - Users can only access their own profile
// - Admins can access any profile
// - Attempts to access unauthorized profiles return 403 ForbiddenTypes of Broken Access Control
Vertical Privilege Escalation
Occurs when a regular user gains access to functionality or data that should only be available to users with higher privilege levels, such as administrators. This includes accessing admin panels by manipulating URLs (e.g., /admin), executing administrative API calls, or bypassing role checks. An attacker with a standard user account might gain the ability to manage users, modify system settings, or access sensitive reports meant only for administrators.
Horizontal Privilege Escalation
Happens when a user accesses resources belonging to another user at the same privilege level. This is commonly achieved through Insecure Direct Object References (IDOR), where attackers modify parameters like user IDs, account numbers, or document IDs in requests. For example, changing ?userId=100 to ?userId=101 to view another user's orders, messages, or personal information. Both users have the same role, but they should only access their own data.
Missing Function-Level Access Control
Arises when applications fail to implement authorization checks on server-side functions, API endpoints, or administrative features. The application may hide these features in the UI but the endpoints remain accessible if discovered. Attackers can directly invoke sensitive functions by crafting API requests or navigating to hidden URLs. Common examples include unprotected DELETE endpoints, administrative API methods, or configuration pages that lack proper authorization middleware. Simply hiding a button or menu item does not prevent access.
Impact
Successful exploitation of Broken Access Control vulnerabilities can have severe consequences for both the organization and its users. The impact varies based on the sensitivity of the exposed resources and the extent of the attacker's unauthorized access.
Attackers can view sensitive information belonging to other users, including personal data, financial records, health information, private messages, and confidential business documents. This can lead to identity theft, financial fraud, privacy violations, and loss of competitive advantage.
By accessing user profile data or administrative functions, attackers can modify account credentials, reset passwords, or completely take over accounts. This allows them to impersonate legitimate users or administrators, performing actions on their behalf and causing reputational damage.
Unauthorized users can modify, delete, or corrupt critical data they should not have access to. This includes altering financial transactions, changing medical records, manipulating product prices, or deleting important business information, leading to loss of trust and operational disruption.
Broken Access Control often results in violations of data protection regulations such as GDPR, HIPAA, PCI DSS, and CCPA. Organizations may face substantial fines, legal action from affected users, mandatory breach notifications, regulatory audits, and long-term damage to customer trust and brand reputation.
Prevention Checklist
Implement a "deny by default" security model where all resources are protected unless explicitly made public. Every server-side function, API endpoint, and route should enforce authorization checks. Never rely on client-side restrictions, hidden URLs, or security through obscurity. Use framework-level middleware or annotations to ensure no endpoint is accidentally left unprotected.
Verify that the authenticated user has permission to access the specific resource being requested, not just that they are logged in. Check ownership (e.g., "Does this user own this order?") or role-based access (e.g., "Is this user an admin?") on every request. Use user session data to validate access rights for each resource ID.
Avoid exposing direct database IDs or sequential identifiers in URLs and APIs. Instead, use UUIDs, GUIDs, or mapping tables to create indirect references that are difficult to enumerate or guess. For example, use /api/orders/f47ac10b-58cc-4372-a567-0e02b2c3d479 instead of /api/orders/1234. This makes automated enumeration attacks significantly harder.
Design and enforce a clear role-based access control model. Define roles (e.g., user, manager, admin) and map each role to specific permissions. Use established frameworks and libraries that provide RBAC capabilities rather than building custom access control logic. Regularly review and audit role assignments to prevent privilege creep.
Configure web servers to disable directory listing on all file directories. Implement proper access controls on file uploads and downloads, ensuring users can only access files they own or have explicit permission to view. Store uploaded files outside the web root when possible and serve them through an access-controlled endpoint.
Log all access control failures with sufficient detail to identify attack patterns, including user ID, requested resource, and timestamp. Set up alerts for repeated authorization failures from the same user or IP address, which may indicate an ongoing attack. Include access control testing in your security testing and penetration testing processes.
Real-World Examples
Facebook Graph API
A broken access control vulnerability in Facebook's Graph API allowed third-party apps to access private photos of up to 6.8 million users, even if those photos had not been posted. The flaw affected photos uploaded to Facebook Stories and Marketplace, as well as photos uploaded but not posted. The vulnerability was active for 12 days before being discovered and patched.
Snapchat
Security researchers exploited a broken access control vulnerability in Snapchat's "Find Friends" API to enumerate and download 4.6 million usernames and phone numbers. The API failed to implement rate limiting or proper authorization checks, allowing attackers to iterate through phone number ranges and retrieve associated usernames. Snapchat had been warned about the vulnerability but did not fix it in time.
First American Financial
A broken access control vulnerability exposed 885 million sensitive real estate documents spanning 16 years. The flaw allowed anyone to access documents by simply changing a sequential digit in the URL, without any authentication. Exposed documents included bank account numbers, Social Security numbers, wire transfer receipts, and driver's licenses. The vulnerability had existed for years before being discovered by a journalist.
Parler
The social media platform Parler suffered a massive data breach when security researchers exploited broken access control in their public API. The platform used sequential post IDs without proper authorization checks, allowing automated scripts to enumerate and download over 70 terabytes of data, including user posts, videos, metadata, and GPS coordinates. This included content deleted by users, which had not been properly removed from the system.
Ready to Test Your Knowledge?
Put what you have learned into practice. Try identifying and fixing Broken Access Control vulnerabilities in our interactive coding challenges, or explore more security guides to deepen your understanding.