According to OWASP, Broken object level authorization (BOLA) presents the largest security threat to APIs. This is due to the common practice of using object identifiers to retrieve and manipulate data via API endpoints. For example, in the URL example.com/profile/1, the 1 is an identifier, which represents the id of the profile to return.
APIs which are vulnerable to BOLA don’t verify whether clients should have access to the data they’re requesting. This allows malicious clients to alter the id of a request to gain access to data in an unauthorised manner.
To combat BOLA, endpoints should perform object-level authorization checks on requests, to check that the client is authorised to access the requested data. For example, if a client requests profile 1 then the API should validate that the client is authorised to access this data, and if not, the request should be rejected.
An approach organisations can take to reduce the exploitability of their API to BOLA is to use globally unique identifiers (GUIDs) as object identifiers instead of integers. GUIDs are randomly generated 128-bit values, which make them very difficult to guess. Using GUIDs negates the ability of attackers to access data in a BOLA-affected API by simply iterating through integers on an endpoint. However, this is only a minor protection which doesn’t actually address the problem of correctly authorising requests, so should only be considered good practice rather than a solution to BOLA.
|Threat agents/attack vectors||Security weakness||Impacts|
|API specific : Exploitability 3||Prevalence 3 : Detectability 2||Technical 3 : Business specific|
|Attackers can exploit API endpoints that are vulnerable to broken object level authorization by manipulating the ID of an object that is sent within the request. This may lead to unauthorized access to sensitive data. This issue is extremely common in API-based applications because the server component usually does not fully track the client’s state, and instead, relies more on parameters like object IDs, that are sent from the client to decide which objects to access.||This has been the most common and impactful attack on APIs. Authorization and access control mechanisms in modern applications are complex and wide-spread. Even if the application implements a proper infrastructure for authorization checks, developers might forget to use these checks before accessing a sensitive object. Access control detection is not typically amenable to automated static or dynamic testing.||Unauthorized access can result in data disclosure to unauthorized parties, data loss, or data manipulation. Unauthorized access to objects can also lead to full account takeover.|
The BOLA issue presents a difficult challenge for APIM to solve. How can the API gateway know whether the client is allowed to access the data they are requesting? API Gateways do not typically have access to the data necessary to make object-level authorisation decisions. This type of data is usually stored in backend databases, where objects are connected to roles and users via their ids. These databases may be located in a network zone which is inaccessible to the API Gateway, and even if they are accessible they may not be practical to access due to performance or security reasons.
However, there are ways for Gateways to make object-level authorisation decisions. But these come at the cost of significant configuration and/or integration. By defining resources based on URIs, objects can be identified and secured. Referring back to the previous example, the URL example.com/profile/1 can be created as a resource, to which access is restricted to clients who have the necessary permissions. There are three main approaches for this:
- Direct integration: Gateways can make authorisation decisions by directly integrating with the backend system, where each request handled by the Gateway is validated against a backend authorisation endpoint. This offloads the authorisation logic onto the backend system, but requires the time and effort to build the integration. It also places additional load onto the Gateway, reducing its overall capacity.
- Identity providers: Identity Providers (IdPs), such as Keycloak, can be configured to manage the resources, scopes, roles and policies needed to perform object-level authorisation decisions. However, this configuration data needs to be transposed into the IdP from the backend system, which presents an onerous task for anything other than small datasets. This data also needs to be kept up-to-date with the backend system, by synchronising any changes as they occur. Alternatively, IdPs can also support the direct integration approach. For example, Keycloak offers an approach called Service Provider Interfaces, which enables it to directly access backend data. This removes the need to import and maintain data within the IdP.
- Policy engines: Policy engines, such as Open Policy Agent (OPA), can be configured to utilise external data when making authorisation decisions. While the problem of data access and maintenance still applies to policy engines, OPA has been designed to work with external data in an efficient manner. There are several approaches possible, which make different trade offs in terms of performance, coupling and consistency.
Each of these approaches needs access to data, which raises the question of whether the task of object-level authorisation is suitable for intermediary components such as Gateways, IdPs and policy engines. It will often be the backend system, not an intermediary component, which is the best place to both access the data necessary for making authorisation decisions, but also understanding the business logic needed to make them. Therefore it’s best practice to perform object-level authorisation within the code of the backend application.
GraphQL APIs present an additional problem in the context of BOLA. GraphQL gives clients the ability to request which fields they want the API to return for an object, and these fields may contain sensitive data which the API owner wants to protect. Without authorisation in place, this type of sensitive data could be accessed by clients. This issue doesn’t quite fit the description of BOLA, since it’s not technically at the object level, it’s a level lower, within the fields of the object, so could be referred to as Broken Field Level Authorisation. Regardless, GraphQL APIs which mix normal data with sensitive data within the same GraphQL types should put measures in place to prevent unauthorised clients receiving sensitive data.
Additionally, Tyk can use its path-based permissions feature to embed specific paths into the authorisation rules for API keys and policies. This gives very fine-grained control over what URLs the consumer can access, but for this use case it’s impractical because of the amount of configuration needed. Each key or policy would need to contain a complete list of allowed paths, including any object identifiers. This data would also need to be updated as the underlying dataset changes, which is the same problem faced by the other approaches which maintain a separate dataset.
Tyk mitigates against the GraphQL issue of exposing sensitive data through fields by providing field-based permissions. These permissions can be added to a policy, granting access to types and fields defined in the API’s GraphQL schema. Clients that wish to query these types and fields will need to be assigned the policy which grants access to them.