API Security: Broken Object Level Authorization

Mar 15, 2021 min read

In this article we’ll dive into the Broken Object Level Authorization (BOLA) API vulnerability. What it is, how it’s created in code, and how to prevent it.

What is Broken Object Level Authorization?

Based on the OWASP 2019 API Security Project, Broken Object Level Authorization vulnerabilities, often also referred to as Insecure Direct Object Reference (IDOR), is the top security vulnerability across APIs today.

API1:2019 Broken Object Level Authorization APIs tend to expose endpoints that handle object identifiers, creating a wide attack surface Level Access Control issue. Object level authorization checks should be considered in every function that accesses a data source using an input from the user. - OWASP 2019 API Security Project

Object level authorization is how we determine which users have access to what information. An object is any information to which the application has access. When an application contains a BOLA or IDOR vulnerability the application has the strong likelihood of exposing sensitive information to the wrong user. Once identified, BOLA vulnerabilities can be extremely easy to exploit, often using simple scripting.

Severity: High Exploitability: Easy Where to detect: Architecture design reviews, code reviews

Code Example

This code example uses the popular NodeJS runtime and ExpressJS framework to explore how a Broken Object Level Authorization vulnerability might look. The NodeJS/ExpressJS combination of technologies has become an industry mainstay in recent years because of its easy implementation, 3rd party library support, and thorough documentation.

The below example is a common GET REST API endpoint to retrieve accounts by accountId. This is similar to other common endpoints like get orders by orderId or get users by userId.

NOTE: Broken Object Level Authorization vulnerabilities do not always look like this, a Broken Object Level Authorization vulnerability is anytime our applications grant unauthorized access to data.

app.get("/accounts/:accountId", authenticateToken, (req, res) => {
  const { accountId } = req.params;

  // A mock database query
  const account = db.accounts.filter((a) => {
    return a.accountId === accountId;
  })[0];

  res.json(account);
});

What is this code doing?

By making a request to this endpoint we are asking for account information where the account ID is equal the :accountId passed in the route. We are checking that the user is “authenticated” through the authenticateToken middleware, we are probably also checking if the user has the “authorization” to “get:accounts”. What we’re not doing is confirming that the account the user is requesting is an account that they have access to, either through account ownership or expressed permission.

Most of the time an endpoint like this is called from a user interface (UI), or after a request to get all accounts and requesting an individual account based on the results of a prior request.

How can this happen?

When our APIs take the shape of our databases, Broken Object Level Authorization vulnerabilities start to appear. For example, in a relational database we use auto-incrementing IDs to represent each row of data in our database table. The ID is typically an integer that helps give efficient database look-ups of data. When we expose that integer value to our API, consumers of our API now have direct access to the underlying data structure. If we forget to ask the question, “can this person get access to this data right now?” we expose our API to a Broken Object Level Authorization vulnerability.

Finding and fixing Broken Object Level Authorization

Broken Object Level Authorization vulnerabilities are not detectible by current security scanning tools. Security tools aren’t able to make the determination that a specific API endpoint should be authorizing a give user each time, this is a task for a human. It is imperative that everyone designing and developing APIs is aware of the vulnerability and is able to identify when such a vulnerability exists. Broken Object Level Authorization vulnerabilities must be identified during the code development process, through regular architecture reviews, code reviews, and evaluating API request logs.

Another way, all be it a reactive solution, is to monitor and detect patterns of requests that iterate over a set of resources. For example, detecting a series of requests from a given user that requests different resources (i.e. data) from the same endpoints in rapid succession. If the data is not related, like in the case of different accounts, there is a potential Broken Object Level Authorization vulnerability that could be exploited.

Designing to prevent Broken Object Level Authorization

Avoiding Broken Object Level Authorization vulnerabilities starts during an applications architecture design and continues through the life of the software. APIs should consider what data should be accessed and by whom. Avoid building APIs that give direct access to data stores by avoiding the use of easily iterable values, i.e. ID = 1, ID = 2, ID = 3 … as API parameters, utilize random unique identifiers instead.

Always check that the data being requested is available for the specific user, not just role, company, or application, and is available to that user at the current time. User authorization can quickly change and we should not assume that was authorized in the past is still true currently.