Microservices Security — Broken Authentication
By: Lal Verma
Tags: Security, Oauth, Software Architecture, Microservices, Owasp

Microservices Security — Broken Authentication

In this article, we are going to decode one of the OWASP Security Threats — “Broken Authentication”, in the context of Microservices Architecture. We will discuss the threat along with the solutions from multiple aspects including o-auth, API gateway, service-service communication, etc.

Photo by Jilbert Ebrahimi on Unsplash

Broken Authentication is the second most severe threat when it comes to OWASP API Security Threats. Microservices Architecture has changed the rules of authentication and authorization to a great extent. We must understand the dynamics, scope, and issues of this threat and address it during our design and development. I am going to cover the topic in four parts —

Authentication Flows — In this section, we will understand the authentication and associated flows. This will help in defining the scope and boundaries of this threat. Authentication Flows in Microservices — In this section, we will understand how the authentication flows typically work in the context of microservices. We will discuss security technologies like O-Auth and Open-ID here. Security Vulnerabilities — In this section, we will understand how our services can become vulnerable to this threat. Security Solutions — In this section we will discuss the solutions to address the security vulnerabilities.

For further discussion on this topic, I am assuming you have a decent awareness of Microservice Architecture, O-Auth, Open-Id, JWT, mTLS, and API Gateway.

Authentications flows

Authentication is the process of identifying users that request access to an application, enterprise, network, or device. The authentication flows are not new but it makes sense to reiterate them in the context of microservices.

Login — This is the most critical and sensitive user flow in any system or application. An end-user is authenticated to a system based on this flow. Typically the user identity is based on username and password but many other variations are in practice these days — mobile-based OTPs, biometric & facial recognition, 3rd party authentication providers, multi-factor authentication, etc. A typical microservices setup will use Identity Server/STS/Open-Id to authenticate the user.

— This is the most critical and sensitive user flow in any system or application. An end-user is authenticated to a system based on this flow. Typically the user identity is based on username and password but many other variations are in practice these days — mobile-based OTPs, biometric & facial recognition, 3rd party authentication providers, multi-factor authentication, etc. A typical microservices setup will use Identity Server/STS/Open-Id to authenticate the user. Subsequent Requests & User Session — We need to ensure all the requests to our application are authenticated. We cannot ask the user for his password every time. In the monoliths, we used to maintain user state through session management but with the microservices, it’s not possible. The typical characteristics of microservices, being independently deployed, restful, and stateless, have pushed the enterprises to adopt token-based access mechanisms such as OAuth 2.0.

— We need to ensure all the requests to our application are authenticated. We cannot ask the user for his password every time. In the monoliths, we used to maintain user state through session management but with the microservices, it’s not possible. The typical characteristics of microservices, being independently deployed, restful, and stateless, have pushed the enterprises to adopt token-based access mechanisms such as OAuth 2.0. Service to Service Communication — This was not the case with monolith applications. Internal modules were bundled and deployed together so there was no need for additional security. With microservices, internal modules get deployed as independent services. Each of these services needs to authenticate itself to the other service(s).

— This was not the case with monolith applications. Internal modules were bundled and deployed together so there was no need for additional security. With microservices, internal modules get deployed as independent services. Each of these services needs to authenticate itself to the other service(s). Password Reset — This flow is as crucial as the “Login” flow in terms of security risks. Similar to the “Login” flow, it also uses multiple variations including OTPs, email verification, security questions, biometric, multi-factor, etc. Implementation of it is mostly unchanged with microservices.

— This flow is as crucial as the “Login” flow in terms of security risks. Similar to the “Login” flow, it also uses multiple variations including OTPs, email verification, security questions, biometric, multi-factor, etc. Implementation of it is mostly unchanged with microservices. Logout — In the typical monolith, once you click “logout” the user is supposed to be logged out from all its services, as its session was invalidated. This is not so straightforward with microservices, as the tokens are used to access them. Depending upon the configuration, they can be valid for a long time.

How does authentication work in Microservices?

Let's try to understand a typical flow in the microservices context with the help of a sample workflow. We have an e-commerce platform where one of the client applications is our website. Backend services are built-in microservices architecture.

User visits our e-commerce store (website) and clicks on “log in”. Our website forwards the request to the OAuth Provider. Let's assume that Google is our OAuth Provider. Google redirects the user to the login page. The user submits the credentials on the login page. Website forwards the request to Google, our OAuth Provider. If authentication is successful, Google will return the access token as part of the response. Our website stores the access_token locally. The User tries to add an item to the cart. The website makes the request to the Shopping Cart Service. Our website does attach the access_token while making the request. Shopping Cart Service makes the request to the o-auth provider(Google) to validate the access_token. If the token is valid, the service starts processing the request, or else the request is rejected. Once the request is processed, Shopping Cart Service sends back the response which is then visible to the end-user.

We just covered a basic scenario and this itself is comparatively complex than our standard monolith flows. Let's extend the discussion and try to understand other fundamental components involved here.

Client credentials —Client applications need to register with the OAuth server to request the access tokens. For example, our e-commerce website needs to be registered with Google(OAuth provider). Once registered, google issues client id and client secret which should be exchanged while making calls to get the token.

—Client applications need to register with the OAuth server to request the access tokens. For example, our e-commerce website needs to be registered with Google(OAuth provider). Once registered, google issues client id and client secret which should be exchanged while making calls to get the token. Open Id — Access token does not provide authentication or user details. OPEN-ID helps in setting the authentication context with the help of “id_token”.

— Access token does not provide authentication or user details. OPEN-ID helps in setting the authentication context with the help of “id_token”. Service to Service communication — We just covered a simple scenario where the request is processed by ShoppingCartService only. What happens when the service needs to call another service. For instance, our ShoppingCartService might call the InventoryService to check product availability. Such “service to service” communications need to be authenticated. This can be based on certificates( mTLS ) or JWT or “ trust the network policy ”

— We just covered a simple scenario where the request is processed by ShoppingCartService only. What happens when the service needs to call another service. For instance, our ShoppingCartService might call the InventoryService to check product availability. Such “service to service” communications need to be authenticated. This can be based on certificates( ) or or “ ” Asynchronous communication — We assumed synchronous communication between applications and services till now. What if the communication is asynchronous where a message broker is involved.

We assumed synchronous communication between applications and services till now. What if the communication is asynchronous where a is involved. API Gateway — Typical microservices setup has API Gateway which provides a single entry point. It interrupts the service requests to cater to authentication, authorization, rate limiting, routing, etc., and separates the business from non-functional aspects.

— Typical microservices setup has API Gateway which provides a single entry point. It interrupts the service requests to cater to authentication, authorization, rate limiting, routing, etc., and separates the business from non-functional aspects. Cross Trust Communication — In big enterprises there could be multiple trust boundaries. For instance, order management vs delivery management could belong to two separate trust boundaries. Services need to authenticated across such boundaries.

I assume the above pointers are good enough to give you a picture of what all components and complexities are involved in microservices, dealing with authentication and related workflows. Let’s try to map the vulnerabilities against the workflows we discussed.

Security Vulnerabilities

Here are the vulnerabilities we will be discussing in detail —

Insecure storage of credentials

Insecure transmission of credentials

Improper token validation

Weak passwords

Credentials stuffing

Brute force attacks

Insecure Storage of credentials

If the authentication credentials are stored in a non-secure fashion, it's easy to get hold of them and exploit the system. This risk is possible with all the credential types —

Passwords stored in the database insecurely.

Security questions/answers for the forgot password stored insecurely.

Tokens are stored in the cookies

Certificate keys or other credentials stored in the code.

Certificate keys or other credentials stored in the container configuration

Insecure Transmission of credentials

If an attacker is able to intercept the user request, he/she can view the sensitive information in the request. Worst case scenario could be when the attacker modifies the request and sends it to the service on your behalf. This risk is possible with any of the credentials —

Sending the password in request insecurely — part of URL, non-HTTPS

Sending secret questions and answers through the non-HTTPS protocol

Sending OAuth tokens as part of the part of URL

Trusting the network when making service to service calls

Passing user context in the API gateway — service or service-service communication

Passing user tokens as part of the message in asynchronous communication

Improper token validation

When it comes to microservices, often the way we are validating tokens is compromised. If tokens not validated properly, this can lead to unauthorized access.

Validating unsigned or weakly signed tokens

Not validating issuer

Not validating token scope

Not validating token’s audience

Validating expired token

Validating tokens even after the user logged out

Weak passwords

When it comes to user credentials, we need to take special care. This is one of the easiest ways to hack the system when the system/enterprise does not enforce strong password policies.

Credentials stuffing

Our system is not protected if we do not safeguard our applications from automated attacks. In Credential stuffing, the attacker acquires the list of usernames and passwords from a website breach. I am sure you would have heard about sony’s data breach.

The attacker then checks each of these accounts in the list against the targetted websites. Even a small number of logins went through successfully can help the attackers to cause a big loss for individuals and enterprises.

Brute force attacks

This is another automated attack that happens when the attacker tries to break into an account by trying multiple passwords. He/She can automate these attacks based on a dictionary or other source where multiple combinations are stored.

Security Solutions

Authentication is the most critical aspect of software and it must be dealt with diligently. Before implementing solutions you should know all the possible authentication flows existing in your system. As a developer, you must do an impact analysis on these flows while implementing new feature sets.

As a developer, you must know your enterprise standards in terms of user and service authentication. You should also be aware of the industry standards to deal with these use cases. The more you stick to the standards, the more you will be risk-free. Reinventing the wheel in case of authentication mechanism could be highly error-prone and maintenance intensive.

Insecure Storage of Credentials

If you are saving passwords in the database, you can use the encryption mechanisms provided by the database providers. For example, MongoDB provides 256-bit Advanced Encryption Standard in Cipher Block Chaining Mode, by default.

If you do not want to use the database encryption mechanism, you can use the application to encrypt and decrypt the values. You can refer to the utility libraries for a specific platform. For java, you can refer to Jasypt, JCA, etc.

If you are saving the tokens in a cookie, use the secure cookie flag. This instructs the browser to only send them over encrypted HTTPS connections.

Do not include any of the keys/credentials in your source code. CI/CD should enable the right credentials at the deployment time. You can also use a centralized configuration service to achieve this.

You can refer to OWASP cheatsheets for more information on secure storage

Insecure Transmission of Credentials

All the network calls should be accessible through HTTPS only — client application to service, service-service, API gateway-service, client— API gateway, etc. This provides a secure connection and guarantees the integrity of the transmitted data. See the Transport Layer Protection Cheat Sheet

The HTTPS protocol is applicable to all the requests authentication flows as well as the functional flows.

Services should not be exposed directly to the client APIs. They should be restricted within the firewall boundaries.

Use API Gateway to implement edge security and provide a single entry point to the system. All the service calls must route through this.

Use OpenId & OAuth 2.0 authentication and authorization frameworks.

Use “mTLS” or JWT for API Gateway to Service communication

Use “mTLS” or JWT for service to service communication.

Improper Token Validation

The relying party or token consumer validates a JWT by verifying its integrity and claims contained. At least the following of the standard claims should be verified.

iss — should be the trusted issuer.

— should be the trusted issuer. aud — validate the right target audience against the requested resource

— validate the right target audience against the requested resource exp — validate the expiry date of the token. it should be active.

There could be a disconnect when the user logs out manually or by the system due to the idle time. Tokens will still be able to access the services if not in-validated. We should blacklist these active tokens.

We will talk more about the validation of tokens with the authorization topic.

Weak passwords

This is an age-old problem and our system should ensure the user passwords are not weak.

Our system must implement weak-password checks.

We should enforce the use of strong passwords based on multiple characters, minimum password length, etc.

Similar to the passwords, we should also ensure that the security questions and answers are weak and easy to guess.

Check out the OWASP cheatsheet on passwords for more details.

Credentials stuffing

This is an automated attack in which the attacker tries to get into the system based on different credentials. You can stop these attacks in multiple ways.

Multi-factor authentication is the best one till now. As per Microsoft's analysis, it's able to reduce the risk by 99%. The Multifactor Authentication cheatsheet available with OWASP contains further guidance on implementing it.

Captcha — The use of an effective CAPTCHA can help to prevent automated login attempts against accounts. Many CAPTCHA implementations have weaknesses, so the use of CAPTCHA should be viewed as a defense-in-depth control to make such attacks more time consuming and expensive.

Security Questions — The addition of a security question or memorable word can also help protect against automated attacks. Furthermore, security questions are often weak and have predictable answers, so they must be carefully chosen. The Choosing and Using Security Questions cheat sheet contains further guidance on this.

Rate Limiting — You can implement “Rate Limiting” to restrict the number of requests from a particular IP address. This can be implemented at the API Gateway layer.

Brute Force Attacks

This is also an automated attack but most targetted towards one single account where thousands of variations of passwords can be applied.

Multi-factor authentication and captcha can help here as well.

Account lockout strategy — This prevents any more login attempts for a period after a certain number of failed logins.

Rate limiting can also help here.

Please refer to the OWASP Forgot Password cheatsheet for more details.

Other Measurements