
What is Broken Access Control?
Broken Access Control (BAC) is a security vulnerability that occurs when an application allows unauthorized users to access restricted resources or perform actions beyond their privileges. It is one of the most critical vulnerabilities listed in the OWASP Top 10 and is a major risk in modern web applications. BAC typically happens when the application fails to properly enforce user role validation, leading to situations where attackers can manipulate requests to gain unauthorized access.
Why is Broken Access Control Dangerous?
Broken Access Control vulnerabilities can lead to:
- Unauthorized Data Access: Attackers can view, modify, or delete sensitive data.
- Privilege Escalation: Attackers can gain higher-level privileges, allowing them to perform actions as an administrator or superuser.
- System Compromise: Attackers can exploit BAC to execute unauthorized actions, compromise entire systems, or escalate their attack.
- Business Impact: Exploiting BAC can lead to legal issues, loss of customer trust, and regulatory fines.
Common Examples of Broken Access Control
1. Insecure Direct Object References (IDOR)

What is IDOR?
IDOR occurs when an attacker can manipulate URL parameters or other input data to gain unauthorized access to resources that should be restricted. This typically happens when the application uses user-controllable data, such as an ID, to access files, records, or other sensitive resources.
Example Scenario:
A user accesses their profile page using a URL like:
http://<domain>.com/profile?id=123
An attacker could change the URL to:
http://<domain>.com/profile?id=124
If there’s no validation to ensure the user can only access their own profile, the attacker might gain access to someone else’s data.
2. Insecure File Upload

What is it?
Improper handling of file uploads can lead to unauthorized access or system compromise. For example, if the application allows users to upload files without validating the file type or content, attackers can upload executable files, which could then be used to compromise the server.
Example Scenario:
A website allows users to upload images, but does not properly validate the file type or content. An attacker uploads a malicious PHP file:
<?php system($_GET['cmd']); ?>
If the server does not properly sanitize the file, the attacker can access the file via a URL and execute arbitrary commands.
3. Privilege Escalation

What is Privilege Escalation?
Privilege escalation happens when an attacker takes advantage of a vulnerability in the application to gain higher access levels, such as from a regular user to an admin. This typically occurs when roles and permissions are not correctly enforced or validated.
Example Scenario:
A normal user tries to access an admin page by modifying the request:
http://example.com/admin/dashboard
If the application does not verify that the user is an authorized admin, they may gain access to admin-level functionality.
Real-World Impacts of Broken Access Control
- Unauthorized Data Access:
- Example: An attacker uses IDOR to access sensitive user information or documents, such as financial records, personal data, or confidential business information.
- Privilege Escalation:
- Example: An attacker changes their role to an admin user and can delete accounts, change user passwords, or access sensitive configurations.
- System Compromise:
- Example: An attacker uploads a malicious file (like a web shell) to gain control over the server, leading to further exploitation or full system compromise.
- Business Impact:
- Example: Exploiting BAC vulnerabilities can result in data breaches, legal liabilities, and fines under data protection regulations like GDPR.
How to Test for Broken Access Control
Manual Testing
- Test User Roles:
- Manually change user parameters, like user IDs or roles, to test if unauthorized users can access restricted resources.
- Example: Modify a URL from
/profile?id=123
to/profile?id=124
.
- Check HTTP Methods:
- Ensure that sensitive actions (like delete, update, etc.) are restricted to authorized users.
- Test by using HTTP methods (POST, PUT, DELETE) inappropriately to check if the system enforces access control.
Automated Testing
- OWASP ZAP:
- Use the OWASP ZAP tool to scan for broken access control and check for permission misconfigurations.
- Burp Suite:
- Burp Suite can be used to manipulate parameters and observe how the application responds to unauthorized requests.
How to Prevent Broken Access Control
1. Implement Role-Based Access Control (RBAC)
- Define roles and permissions clearly and ensure each role has appropriate access. Example in code:
if user.role != 'admin':
raise PermissionError('Access Denied')
2. Use Server-Side Validation
- Never trust the client-side for access control. Always validate access on the server-side to ensure users cannot modify their roles or access other users’ data.
3. Enforce Proper Access Control on All Resources
- Ensure that every resource (URLs, files, data) has strict access control policies, preventing unauthorized access.
- Example:
python if current_user.id != requested_user.id: raise PermissionError('Access Denied')
4. Avoid Predictable URLs
- Do not expose user IDs or other sensitive data in URLs. Use hashed or randomized identifiers to protect against IDOR.
- Example:
plaintext http://example.com/profile?uid=abc123
5. Implement Logging and Monitoring
- Continuously monitor access control policies and log access attempts to detect and prevent unauthorized access.
6. Minimize Privileges
- Ensure users and applications operate with the least privilege necessary. For example, if a user only needs read access, do not grant write or delete access.
7. Regular Access Control Audits
- Regularly review and update access control rules and configurations to ensure they are properly enforced.
Practical Example: Preventing Broken Access Control
Scenario:
A website allows users to view their own profile, but an attacker tries to access another user’s profile by modifying the user ID in the URL.
Vulnerable Code:
@app.route('/profile')
def profile():
user_id = request.args.get('id')
user = get_user_by_id(user_id)
return render_template('profile.html', user=user)
How to Fix:
@app.route('/profile')
def profile():
user_id = request.args.get('id')
if current_user.id != int(user_id):
raise PermissionError('Access Denied')
user = get_user_by_id(user_id)
return render_template('profile.html', user=user)
In the fixed version, the application checks if the current_user
is the same as the user whose profile is being requested. If not, access is denied.