Broken access control is the fifth most critical vulnerability, according to The Open Web Application Security Project. Occurring in a whopping third of all web applications, this high-stakes flaw enables resources (such as API endpoints allowing transactions to be executed without proper authorization) to be accessed by merely calling them directly, even without references from web applications.
Unfortunately, regular security audits cannot catch vulnerabilities in API layer validation and access control. In an object-oriented world, API layer bugs tend to replicate on multiple endpoints. Since manually testing access control tends to be time-consuming and complicated, it’s a good candidate for automation.
In this blog entry, I will share a sample scenario of broken access control, and then recommend strategies for intelligently addressing this problem.
A sample broken access control scenario
When an administrator attempts to modify a user’s role, an HTTP request containing collected information is sent to the server to let it know that the administrator has made a change that needs to be remembered. Let’s use a simplified request to illustrate it:
POST https://example.com/api/user-role HTTP/1.1 Content-Type: application/x-www-form-urlencoded Authorization: Bearer eyJQcm9wZXJ0eSBvZiBKdXN0YXMgTGF1emFkaXMifQ== userId=123&userRole=1
A user could easily catch and review the actual request using some kind of proxy or even the built-in browser developer tools. Furthermore, a user could perform the same request without using a web application, instead using any HTTP client (for example, user-friendly Postman). This sample request could be freely modified to edit other user roles:
POST https://example.com/api/user-role HTTP/1.1 Content-Type: application/x-www-form-urlencoded Authorization: Bearer eyJQcm9wZXJ0eSBvZiBKdXN0YXMgTGF1emFkaXMifQ== userId=124&userRole=1 POST https://example.com/api/user-role HTTP/1.1 Content-Type: application/x-www-form-urlencoded Authorization: Bearer eyJQcm9wZXJ0eSBvZiBKdXN0YXMgTGF1emFkaXMifQ== userId=125&userRole=1
You can see that the only difference between these requests is the value of the userId parameter. The other parameter, userRole, represents the user role to which the requests are attempting to change the current role. Note, the request itself does not carry any information of user’s current state and relationship between a user and the administrator.
Imagine that the administrator performed these three requests, and received responses saying the changes were saved successfully. But, then they noticed that they could not manage one of these users via the UI due to business logic restrictions (for example, one of the users does not fall under administrator’s department). Who would be responsible for catching such a bug? And, is an administrator the only one who could make this kind of change? Could anyone make themselves an administrator? Is it possible to determine which request should have been invalidated and blocked on the server side? Not unless the business logic is known, which is not the case with most security audits.
To support the user role example, some algorithm should know how to request something from the server to understand the relationship between the administrator (who edits) and the user (who is being edited). Furthermore, an action request could contain a lot of parameters, some of which could be dependent, and should require some prerequisites. These circumstances increase the complexity and make it impossible to scan for broken access control without knowing predefined rules.
Knowing predefined rules allows for the creation of custom methods to scan against such vulnerabilities. In most cases, it's relatively easy to automate access control test scenarios as integration or end-to-end tests. Still, you should also test for vulnerabilities manually. After all, newly introduced endpoints or changes to existing ones could result in the discovery of new scenarios.
Before starting a system audit from the perspective of access control, acknowledge security through obscurity. By definition, security through obscurity states that one cannot cause harm unless they know the exact access point. So, basically, it's based on the assumption that the access point cannot be guessed or found, if it was not meant for the user to find it. However, this assumption has nothing to do with real-world eb applications. In reality, ‘secret’ endpoints could either be located (because of other vulnerabilities) or guessed (as they usually follow the pattern of other access points and reuse terms from the UI).
The composition of a quality access control
As stated above, much of access control is about predefined rules. These rules are usually made up of (but not limited to) several key points. Knowing that, keep the following in mind when planning how you will best address proper access control:
Groups: Are entities inside the system grouped in any way (e.g. by user role, location, status, or another parameter)? Can a user access or perform actions on group entities from outside the group? If some information has different access levels between groups, is that reflected within API responses? If the selection of some parameter is limited to the UI, does the back-end respect the same limitation?
Dependencies: Do entities inside the system contain dependencies to other entities? Do entities depend on the state of other entities? Should the user be able to delete or modify an entity while another one depends on it? Would the system be able to handle an entity properly if the status of its related entity has changed?
Input logic: Does the system check if all required parameters are being submitted? Does the system check if the parameters being submitted meet input logic?
Directional flows: Does the system contain any entities that should flow from one state to the other strictly in one direction? If so, does it really forbid bringing those entities back to previous states in the opposite direction? Does it validate if the entity has the right status to perform the transition or some action requiring that status?
Broken access control is a common and frequent vulnerability of web applications. Regular security audits are currently unable to tackle such vulnerabilities, so development teams should take ownership of this responsibility. As a test engineer, I found it most efficient developing an access control testing strategy combining both automated and manual methods.