Part 1 of this two-part series discussed what services and microservices are, the role of APIs and API gateways in modern application architectures, the importance of user-level security context, and end-to-end (E2E) trust. Now part 2 covers authorization and different ways of handing it across microservices, what authentication (AuthN) and authorization (AuthZ) protocols to use, and what to do when an API is invoked by applications and services outside its trust boundary. It also covers considerations for additional security policies beyond AuthN and AuthZ, logging and monitoring, and how group policies can help build a more secure app that is based on APIs and microservices.
Some exposure and previous knowledge of APIs and microservices-based architectures help you better grasp the security aspects discussed. However, it is not necessary. For a better understanding, make sure to read Part 1 of this series.
Take about 30 to 45 minutes to read both parts of the series. Part 2 should take about 15-20 minutes.
The need for AuthZ
Part 1 discussed authenticating users and enforcing the required security policies across the microservices of an application at the API gateway. In addition, each microservice might have specific AuthZ requirements. For example, in an online banking application, a microservice for bank accounts should only allow read operations to a checking account for users with basic authentication context. But the checking account should also allow the users to complete both read and write operations in a two-factor authentication (2FA) authentication context.
The security token service evaluates the information about the service or resource that is requested (typically the HTTP URL location of the target service or resource), the scope or type of request, and each user’s security context. Then it issues a security token that represents the AuthZ issued, including valid claims that are made. The set of claims can include the end-user and issuer’s identities, identities of specific consumers, expiration time, and more.
Consider an architecture where a token-exchange service obtains a security token for each request, as shown in the following flow diagram. The service can handle AuthZ aspects, such as whether a request type (for example, READ) in a specific user-level security context is allowed for the requested service or resource. It can add this information as valid claims and scopes in the token.
The microservice that receives the security token with the service request verifies the authenticity of the token to ensure it is issued by a trusted service. Then, it provides the functionality requested by the sender, based on the valid authorized claims and scope. If you need additional microservices to complete the invocation, use the token-exchange service to obtain a new security token (which the downstream microservice can use with the appropriate protocol, claims, and scope).
If the API gateway issues one E2E trust token for the entire journey across one or more microservices and no token-exchange service is used downstream, each of the microservices across the call chain must handle all AuthZ related aspects. In this situation, make sure that each microservice is configured with the appropriate AuthZ policy and that the policies are enforced correctly. With this approach you don’t need to get a new security token each time. However, you get more flexibility and better access control (as discussed in part 1) with central handling of AuthZ and the token-exchange end-point.
What AuthN and AuthZ protocols can I use?
You can also use the SAML protocol for API security, and it supports both AuthN and AuthZ. SAML gained popularity with the rise in adoption of web services inside organizations, and it became the standard of choice for service-oriented architectures. However, due to its heavy nature and requirements for XML and SOAP, teams working with external REST APIs that are based on HTTP opted for the lighter OAuth 2.0 and OpenID Connect protocols.
Invocation by external applications and services
In some cases, an application provides functionality to the user that might require invoking a microservice provided by another application. For example, a mash-up “account balance” microservice in an online banking application presents one view of all of a user’s account balances, including checking and mortgage accounts. The microservice needs to obtain this information from separate core banking and mortgage systems respectively.
The mortgage application’s microservice is invoked by the account balance microservice that resides in another application trust zone (in this case the online banking application) where the user authentication step is already completed. The mortgage application might not use the same identity provider (IdP) as the online banking application, and the goal is to not require the user to go through “heavy” authentication again. Therefore, there needs to be a mechanism to pass the user’s security context and valid claims in a way that the requested microservice in the mortgage application can verify and use it.
In addition, the security token issued by the online banking application’s security-token service proves that the user successfully met the conditions required by the security policies of the online banking application. However, the mortgage application might have different security policy requirements that need to be satisfied.
In this situation, you need to establish some form of trust between the trust zones for the two applications to verify both the authenticity of the claims and the user’s security context that is presented by the security token in the request. You can use the security-token service of the called application to verify the token’s signature, and you can issue a new token that can be verified by the microservices of that application. Also, the requested microservice’s API gateway can enforce any additional security policies that are required before the request is passed downstream.
Without central handling of these activities by an API gateway and a security token service, each microservice would need a trust relationship with external consumers, causing a very complex and unmaintainable architecture.
The API gateway of the called application should enforce all required security policies, unless it is specifically indicated that a security policy was satisfied by the trusted caller. You cannot always trust that external applications performed the required security checks, so follow a defense-in-depth strategy that applies security at all layers.
Should I care about other security issues?
So far, this article primarily focused on enforcing security policies related to AuthN and AuthZ concerns. However, the API gateway, in addition to acting as central a point of such enforcement, should also apply other security policies. Consider the following options:
- Rate-limiting prevents calling of an API more than the allowed number of times for a given period by a specific consumer.
- JSON threat protection protects against content-level attacks that attempt to use structures that overwhelm JSON parsers, resulting in app level denial-of-service.
- XML threat protection protects against attacks related to unusual inflation of elements, attributes, and nesting levels (similar to JSON threat protection).
- Other custom policies centrally address applicable application security threats.
Rather than each microservice development team creating its own policies, the policies should be provided centrally, so teams can configure and apply what is needed at the API gateway. This approach eases the development process and ensures consistent enforcement. You still might need to develop custom policies (if they don’t already exist) to meet specific needs.
Don’t forget to log, monitor, and detect
As with all security architectures, detective controls such as logging and monitoring play an important role as well. Each API needs to log all important events including security-related ones and send the data to a central system for further correlation, analysis, and detection of potential security concerns.
Record security related events, such as a success or failure of compliance to a specific policy, for further analysis and threat detection. Collect information, like how many times end-points are invoked and response times, to assist your team in both preventing denial of service and in better profiling the application and system.
To help detect fraud, APIs can act as instruments to provide data about the specific device that accessed the API (and its location). Use this information to detect scenarios that match a specific fraudulent access pattern.
Apply group policy
Grouping related objects into one unit and applying configuration values or policies across the group is not a new concept. This approach helps you consistently apply those values and policies to the specific set of objects in that group. The same principle applies to the microservices of an application. Group together the microservices and their APIs that address a particular business need. Present them to developers with a service plan and set of policies that apply to that group.
For example, consider a set of APIs that provide data in XML format, used for building an internal application for account servicing by staff. These APIs require an authentication policy that integrates with the corporate active directory (AD) service, XML threat protection, and a rate-limiting policy that focuses on inside initiated access.
Consider another set of APIs with a JSON data format, used for building an external application for customers to perform Internet banking. These APIs require an authentication policy that uses the customer data repository (for example, LDAP), enforcement of JSON threat protection, and rate-limiting designed for external use cases.
After all security policies applicable to the group are successfully enforced, access is provided and limited only to specific microservices or resources within the group, which is indicated by the security token. When individual microservices have additional security policy requirements over and above the group policies, handle the custom policies either at the API gateway with the security token service, at the token exchange service, or at the microservice itself.
Modern applications based on APIs and microservices are often distributed and communicate over networks. But you need the same level of security assurance as you do in monolithic applications. API gateways help with consistent enforcement of security policies across the microservices of an individual application and can assist with handling aspects related to authorization.
It is also important to have user-level security context and E2E trust across the entire journey, in addition to service level trust among the microservices of an application. You can use protocols such as OpenID Connect, OAuth 2.0, and SAML to facilitate AuthN and AuthZ, and aid in designing a system that handles security at the right place and the right time and guarantees end-to-end trust across the entire journey.
This article also covered why you should apply other security policies beyond AuthN and AuthZ (for example JSON threat protection and rate limiting), the importance of appropriate logging and monitoring, and using policies at the group level to build more secure applications that are based on APIs and microservices.
In summary, consider the following security concepts when you design and implement microservices and API-based applications or services:
- Maintain user-level E2E trust across the entire journey.
- Ensure AuthZ is enforced at the right place with the right level of granularity.
- Group your APIs and use an API gateway to apply configurable security policies consistently.
- Don’t forget to log, monitor and detect.
- Follow a defense-in-depth strategy, and add security at all layers.