Broken Access Control
Broken Access Control at a glance
Overview
Broken access control occurs when authorization checks are missing, inconsistent, or enforce the wrong principal. This includes endpoints with no role checks, object level permissions that are not validated, misuse of client provided claims, mass assignment of privileged fields, and misapplied method level security. Attackers exploit these gaps to access or manipulate data they should not reach.
Where it occurs
Common places are admin endpoints missing middleware, object detail endpoints that accept ids without ownership checks, APIs that accept tenant or team ids without membership verification, controllers that mass assign attributes, and code that trusts client side roles or unsigned tokens.
Impact
Impact ranges from disclosure of sensitive data to full privilege escalation. A single missing check on a high impact endpoint can allow account takeover, mass data export, or manipulation of financial or product state.
Prevention
Adopt a deny by default model. Enforce authorization checks server side for both coarse grained roles and fine grained resource ownership. Use framework or language best practices for method level guards and resource policies. Avoid mass assignment, and treat any attribute that changes privileges as admin only. Validate tokens and claims cryptographically and map them to server side authoritative records.
Examples
Switch tabs to view language/framework variants.
Spring, service method exposed without @PreAuthorize
Controller calls a service method that assumes the controller checked roles. If any other caller reaches the service, unauthorized actions occur.
import org.springframework.web.bind.annotation.*;
@RestController
class UserAdminController {
@PostMapping("/promote")
public String promote(@RequestParam Long id) {
userService.promoteToAdmin(id); // BUG: no @PreAuthorize
return "ok";
}
}
@Service
class UserService{
public void promoteToAdmin(Long id) { /* grants admin role */ }
}- Line 5: Service method performs privileged work without explicit method level guard
Authorization assumptions at the controller layer break when code is reused. Privileged logic must enforce authorization at the service boundary.
import org.springframework.security.access.prepost.PreAuthorize;
@RestController
class UserAdminController {
@PreAuthorize("hasRole('ADMIN')")
@PostMapping("/promote")
public String promote(@RequestParam Long id) {
userService.promoteToAdmin(id);
return "ok";
}
}
@Service
class UserService{
@PreAuthorize("hasRole('ADMIN')")
public void promoteToAdmin(Long id) { /* grants admin role */ }
}- Line 4: Apply @PreAuthorize at controller and service to ensure enforcement
Use method level guards or central interceptors so authorization checks cannot be bypassed by calling services internally.
Engineer Checklist
-
Enforce authorization for every privileged route and resource
-
Scope DB queries by authenticated principal for object ownership
-
Use method level guards or resource policies, not only controllers
-
Whitelist updatable attributes, do not mass assign role flags
-
Verify tokens signatures and map claims to server side records
-
Add audit logs for high privilege actions and alert on unusual patterns
End-to-End Example
A multi tenant app exposes /teams/{id}/projects and returns projects for any id parameter. The attacker enumerates ids and harvests projects across tenants because the server does not check membership.
[HttpGet("/teams/{id}/projects")]
public IActionResult GetTeamProjects(int id) {
// Bug: no membership check - any authenticated user can access any team's projects
var projects = db.Projects.Where(p => p.TeamId == id).ToList();
return Ok(projects);
}[HttpGet("/teams/{id}/projects")]
public async Task<IActionResult> GetTeamProjects(int id) {
var userId = User.FindFirst("sub").Value;
// Verify the authenticated user is a member of the team
if (!await teamService.UserIsMemberAsync(userId, id)) {
return Forbid();
}
var projects = db.Projects.Where(p => p.TeamId == id).ToList();
return Ok(projects);
}Discovery
Test if endpoints lack authorization checks by accessing admin/privileged functionality with regular user credentials.
-
1. Test unprotected admin endpoint
httpAction
Access admin dashboard without admin role
Request
GET https://app.example.com/admin/dashboardHeaders:Authorization: Bearer regular-user-tokenResponse
Status: 200Body:{ "html": "<html><title>Admin Dashboard</title><h1>System Statistics</h1><p>Total Users: 125,430</p><p>Revenue: $12.5M</p>...", "note": "Admin dashboard accessible without admin role check" }Artifacts
broken_access_control admin_dashboard_exposed missing_authorization -
2. Test admin API without role check
httpAction
Call admin-only API endpoint with regular user credentials
Request
GET https://app.example.com/api/admin/usersHeaders:Authorization: Bearer regular-user-tokenResponse
Status: 200Body:[ { "id": 1, "username": "admin", "email": "admin@company.com", "role": "admin", "api_key": "sk_admin_xyz789" }, { "id": 2, "username": "john", "email": "john@company.com", "role": "user" }, "... (125,430 user records including admins and API keys)" ]Artifacts
user_enumeration admin_account_disclosure api_key_exposure -
3. Test write access without authorization
httpAction
Attempt to modify system settings as regular user
Request
PUT https://app.example.com/api/admin/settingsHeaders:Authorization: Bearer regular-user-tokenContent-Type: application/jsonBody:{ "maintenance_mode": true, "allow_signups": false }Response
Status: 200Body:{ "message": "Settings updated successfully", "settings": { "maintenance_mode": true, "allow_signups": false }, "note": "System now in maintenance mode, new signups disabled" }Artifacts
system_configuration_access service_disruption admin_functions_accessible
Exploit steps
Attacker with regular user account accesses admin functionality due to missing authorization checks, gaining full administrative control.
-
1. Access admin panel and enumerate privileges
Explore admin functionality without admin role
httpAction
Access admin endpoints to map available privileged functions
Request
GET https://app.example.com/admin/functionsHeaders:Authorization: Bearer regular-user-tokenResponse
Status: 200Body:{ "available_functions": [ "/admin/users - User management", "/admin/settings - System configuration", "/admin/billing - Payment and subscription management", "/admin/logs - Access and audit logs", "/admin/database - Database console" ], "note": "All admin functions accessible without authorization check" }Artifacts
admin_function_enumeration unrestricted_access attack_surface_mapping -
2. Delete user accounts and data
Use admin delete function to remove accounts
httpAction
Delete competitor or admin accounts without proper authorization
Request
DELETE https://app.example.com/api/admin/users/1Headers:Authorization: Bearer regular-user-tokenResponse
Status: 200Body:{ "message": "User admin (ID: 1) deleted successfully", "deleted_user": { "username": "admin", "email": "admin@company.com", "role": "admin" }, "cascade_deleted": "18 related records (sessions, API keys, audit logs)" }Artifacts
admin_account_deleted data_destruction service_disruption -
3. Access database console for direct manipulation
Use admin database console without authorization
httpAction
Execute arbitrary SQL queries via admin database interface
Request
POST https://app.example.com/admin/database/queryHeaders:Authorization: Bearer regular-user-tokenBody:{ "query": "UPDATE users SET role='admin' WHERE id=9999; SELECT * FROM api_keys;" }Response
Status: 200Body:{ "query_results": [ { "rows_affected": 1, "message": "User 9999 upgraded to admin" }, { "results": [ { "service": "Stripe", "key": "sk_live_51HxYz..." }, { "service": "AWS", "key": "AKIAIOSFODNN7EXAMPLE", "secret": "wJalr..." }, { "service": "SendGrid", "key": "SG.xYz789..." } ] } ] }Artifacts
database_access privilege_escalation api_key_theft complete_compromise -
4. Modify billing and steal payment information
Access billing admin to view/modify payment data
httpAction
Access customer payment information via unprotected admin billing endpoint
Request
GET https://app.example.com/admin/billing/customersHeaders:Authorization: Bearer regular-user-tokenResponse
Status: 200Body:{ "customers": 15430, "sample_records": [ { "customer_id": "cus_ABC123", "name": "John Smith", "email": "john@company.com", "card_last4": "4242", "stripe_customer_id": "cus_...", "lifetime_value": 45000 }, "... (15,430 customer payment records)" ] }Artifacts
payment_data_access customer_pii financial_information pci_compliance_violation
Specific Impact
The attacker exfiltrates internal project data from multiple teams. Sensitive information including project names, budgets, and owners is exposed. Depending on the data, this can enable corporate espionage or targeted phishing.
Remediation requires updating APIs to check membership, rotating any leaked secrets, and notifying impacted customers. Detection is slow because ordinary queries look similar to legitimate ones.
Fix
Check that the authenticated principal is authorized to access the resource before returning data. Centralize membership checks in a policy service and add tests and CI gates that fail on endpoints returning data for arbitrary ids.
Detect This Vulnerability in Your Code
Sourcery automatically identifies broken access control vulnerabilities and many other security issues in your codebase.
Scan Your Code for Free