Michał is a Product Marketing Engineer at Curity. He is a developer with over 10 years of experience in web technologies. Working on different projects allowed him to learn different languages and observe different approaches to design patterns – also regarding APIs.
APIs are everywhere these days, there’s no doubt about it. As with almost all products, however, many aspects of APIs become more complex as they develop. Organizations often use APIs that are small enough to be easily managed and secure. For many others, however, the API landscape is growing exponentially, introducing new challenges to consider.
It should be noted that such an expansion can occur in different directions. For example, APIs are growing in depth in businesses that rely heavily on microservices. A request to a publicly exposed endpoint ends up being passed to many different services, either in parallel, when one service aggregates data from a few others, or in a chain, where one service calls another, which in turn is turn calls for another, etc. APIs can also be extended. Sometimes an API exposes dozens or even hundreds of endpoints through a single entry point.
As API complexity increases, security solutions often remain too simple to sufficiently secure a larger ecosystem. Although many companies have developed their API security with access tokens issued using OAuth, the exclusive use of OAuth and access tokens may not be sufficient for large business landscapes. ‘API. Often, all access tokens issued for an API have the same properties, which means that a bearer of one token has the same privileges as a bearer of another token. This can cause some problems.
Extended API issues
If an API exposes many endpoints, the problem is that the bearer of such an access token can successfully invoke any endpoint. Usually, when appropriate authorization controls are in place, the holder of such a token will not be able to retrieve another user’s information. However, such support may be able to call endpoints that you may not want them to do. For example, a client that should only be allowed to read user data – perhaps because the documented endpoints only allow read access – starts calling endpoints to update them. user data.
Some companies try to solve this problem by introducing scopes that limit the capabilities of an access token. This is a possible solution, but it can lead to additional problems. For large APIs, it’s usually difficult to develop a good set of scopes that divide features into logical groups. It is also necessary to manage the scopes and the endpoints to which they correspond. Large APIs are vulnerable to the scope explosion, a situation where too many scopes are created. This makes access control difficult to manage and confuses users, who might not understand the client’s permissions.
When scopes allow access to large parts of your API, the same problem described earlier can occur: a client can call resources with a given scope even if you don’t want to.
Depth-Grown API issues
Some APIs have a “deep” architectural design, in which many services are called when processing a request. When a token is used to secure all downstream services, the following situation can occur:
- Service A calls service B with the access token received from the caller.
- Service B can use the token to properly grant authorization to the caller.
- Now, Service B can also call any other endpoint in the API and perform an action that only the original client was authorized to perform.
This situation can become particularly dangerous if the downstream services include third-party services outside of your infrastructure. In such a situation, not only does the token leave your infrastructure, but it is now in the hands of a third party, who can use it to call your API.
Consider how access tokens and their content are viewed. (This is not a security issue per se, but it can affect architectural choices). With access tokens, we often try to discern who the user is calling the API and whether that user is authenticated (logged in). However, we must remember that different clients can call APIs. Even though a user has given consent to a client to act on their behalf, the user may not be exploiting the client at all – it may be a background process, for example.
With this in mind, access tokens should not be seen as similar to a user’s browsing session. When dealing with access tokens, you don’t have to worry about identity, but rather that the access token responds to what the bearer can do with that access token, or on behalf of which user l action is performed.
For example, a user can give their consent to an application to aggregate their calendars. The app will call the background calendar API from a backend service. Therefore, the Calendar API should not assume the identity of the user on the sole basis of the subject’s claim of the access token. The API should be aware that the user may not have initiated every request, even if they contain all of the user’s credentials.
Authorization based on complaints
Scopes are a type of claim commonly used in access tokens, but they shouldn’t be the only source for authorization decisions. The audience claim (“aud”) is a good example of another claim that systems can use to easily restrict API access. With the audience claim, the API can check if the access token was intended for a specific audience and reject tokens intended for a different audience. Authorization servers are able to issue requests based on complex information from the authentication process. Each API can then use these claims to make advanced authorization decisions.
For example, the Calendar API described earlier could use claims containing information about the client to know whether a request is from a background service or from a mobile application operated by the user.
You can learn more about best practices for designing scopes and claims or check out how complex claims can be issued in Curity Identity Server.
Implement token sharing approaches
It is crucial that in large APIs, especially those that are very deep, the token is not reused in all of the services invoked to process a request. Teams should consider implementing token sharing in a way that best suits the current situation.
This usually means implementing token exchange, a standard that allows a service to gain new access based on its current one. The token exchange limits the potential actions that the downstream service can take with the token. However, integrating an access token may also be a viable option for some businesses.
Manage authorization complexity
In large API ecosystems, managing authorization rules can become tricky. Each service, regardless of whether it is handling the incoming request or whether it is called by another service, needs to know which complaints to consider and what actions should be allowed based on those complaints. Getting it wrong leads to a broken function-level authorization, which in 2019 was among the top 10 vulnerabilities of the OWASP API.
To resolve this issue, companies can implement rights management systems to help manage authorization complexity and separate issues with centrally managed authorization rules. Open Policy Agent is an example of an open source authorization engine.
Conclusion: How to secure APIs
Security is always important and difficult to achieve, and APIs are no exception. When securing APIs, solutions that work for small deployments may be insufficient in large ecosystems. The safety functions should also not be adapted only to the type of product in question.
Implementing a feature just because it’s recommended for APIs may not always be enough. Scale of operation should also be taken into account when making architectural decisions, as it will make certain functionality more or less critical for a system or introduce the need for a more intrinsic configuration. When designing APIs, companies should consider these points to appropriately choose the right security solutions from the options available.
Featured image via Pixabay.