OAuth 2.0 — Security Considerations
Background
Previously, the common way for granting authenticated access to web applications was through the use of a username and a password. Every time you browse through a new website, you need to create a separate account for each one of them. From a security point of view, it is best to use different passwords for different applications because in case any of the accounts get breached, no other apps will be affected.
But it is much of a hassle to remember varying passwords for varying apps, so most of us go with the password reuse option. Because of these complications, a need was felt to create the central authority which will help to authorize the users to any third-party apps instantly without making any separate account and this is how OAuth was born.
While browsing the web, you’ve almost certainly come across sites that let you log in using your social media account. The chances are that this feature is built using the popular OAuth 2.0 framework.
Introduction
OAuth can be defined as an authorization framework that enables web/desktop or mobile applications to request limited access to a user’s account on another application. Users need not expose their username/password in this process of granting access to the requesting application. Moreover, users can fine-tune which data they want to share rather than having to hand over full control of their account to a third party.
The basic OAuth process is widely used to integrate third-party functionality that requires access to certain data from a user’s account. For example, an application might use OAuth to request access to your Facebook account’s profile picture so that it can be set as an avatar in that app.
Originally developed for the purpose of authorization (sharing access to specific data between applications), OAuth has emerged to serve as an authentication mechanism, allowing users to log in with an account that they have with a different website.
Working Mechanism of OAuth
OAuth consists of four distinct parties, namely a client application, a resource owner, resource server, an authorization server, and there occurs a series of interactions between these parties to make the authorization/authentication process work.
- Resource Owner: The
user
who authorizes anapplication
to access his account. Basically, the person who wants to use the service. - Client: The client is the
application
that wants to access theuser’s
account. Before it may do so, it must be authorized by the user, and the authorization must be validated by the API. - Resource Server: The resource server hosts the account of the user containing private information in a protected manner.
- Authorization Server: The authorization server verifies the identity of the user then issues access tokens to the
application
.
Technically, both the resource server and authorization server roles can be fulfilled through a single service’s API which we can call OAuth Service Provider.
Among different methods (we call them flows or grant types) of implementing OAuth, two of them are the most prominent: “authorization code” and “implicit” grant types. In general, both of the flows consists of the following interactions:
- The client application requests access to a subset of the user’s data, specifying which grant type they want to use and what kind of access they want.
- The user is prompted to log in to the OAuth service and explicitly give their consent for the requested access.
- The client application receives a unique access token that proves they have permission from the user to access the requested data. Exactly how this happens varies significantly depending on the grant type.
- The client application uses this access token to make API calls fetching the relevant data from the resource server.
In this article, we will be discussing the “authorization code” flow mechanism but the idea is the same with “implicit” flow too.
Authorization code grant type
Let us discuss the flow in detail.
- Authorization request : The client-app (e.g, Canva) sends a request to the OAuth service’s /authorization endpoint asking for permission to access specific user data.
- User login and consent: After receiving the authorization request, it will redirect the user to a login page, where they should authenticate themselves using the credentials of OAuth provider (e.g, Facebook account). After successful login, the user is presented with list of data that the client-app (Canva) wants to access. If everything is fine, the user gives consent to this action.
- Authorization code grant: After the consent by the user, the OAuth provider will provide the client-app (Canva) with a “authorization code” with the help of which it should now request for access-token. All the communication from now will happen on server back-channel.
- Access token request: The client-app on the server side exchanges the “authorization code” with OAuth provider (Facebook) to gain the access token.
- Access token grant: If everything is as expected, the access token with the requested scope will be granted by the service provider (Facebook).
- API call: Since the client-app now have an access token in hand, it can finally fetch the user’s data from the resource server (Facebook server).
- Resource grant: If the token is valid, the resource server will provide the client-app (Canva) with the selected resource.
Causes for OAuth 2.0 vulnerabilities
OAuth specification is ambiguous and flexible by design because of which a lot of vulnerabilities creep in. Although some of the configurations are obligatory for fundamental operations, the great part of the implementation is still optional that includes many settings required for keeping user’s data safe.
Also, OAuth lacks built-in security mechanisms. Almost all the security arrangements are made by the developers using the right choice of settings and implementing additional layers of security through validation and escaping. There come high chances of vulnerabilities to seep in if the developer is inexperienced with OAuth.
From the above flow, one can see there involves interaction between a lot of parameters. Failing for validation of any of them may lead to OAuth vulnerability leading to serious issues like Account Takeover.
The causes for these vulnerabilities can be divided into two sections:
- Client-Application (client-side) misconfigurations
- OAuth Service (server-side) misconfigurations
Client-Application misconfigurations
Though developers will use well-known and rigorously-tested OAuth service which is likely to have no issues at all, the implementation in the client-app may contain some misconfigurations. Due to the fact that many different parameters need to be taken into consideration, weak validation at any point may lead to a serious failure and high chances of critical vulnerabilities. Some of the client-app misconfigurations include:
Access Token Reuse in Implicit Grant Type
In implicit grant type, access tokens are sent via the browser because of simplicity in implementation which opens up a problem if not configured properly. Once the user gets logged in using the OAuth flow, the application may want to maintain the session with this user even after he closes the browser. So what the client-app can do is simply submit the username and this access token as a traditional password via a POST form and assign this user with a cookie. Simple!
However, if we look closer, there can be a flaw. Since the app doesn’t have any secrets to compare with this submitted data server-side and also the token is exposed to the end-user, it may be reused with another username which can effectively log in the user in some cases.
If the application doesn’t tie the access token to the specific user, then this vulnerability may arise which is a serious issue.
Flawed CSRF Protection
In order to ensure defense against Cross-Site Request Forgery (CSRF) attacks, OAuth uses the ‘state’ parameter containing an unguessable value and tied with the user’s session (most important). However, if no ‘state’ parameter is used at all then this is extremely interesting from an attacker’s perspective. We are talking about the “authorization code” grant in this example.
This effectively means that the attacker can initiate the OAuth flow themselves at first until the authorization code grant phase. The authorization server will provide the client app with an URL containing the authorization code which needed to be exchanged with an access token. The URL looks like this:
https://vulnerable-site/callback?code=<CODE>&[state=missing]
The above URL is vulnerable to CSRF attacks because it is not tied to any session. Then this URL will be delivered to the victim with the help of any innocent-looking page which will cause the app to link the attacker’s social media account with the victim’s account on the vulnerable application. This means, when the attacker goes with the ‘Login with Social Media’ option, the victim’s account will be logged in.
OAuth Service misconfigurations
Authorization code and access token leakage
OAuth uses a ‘redirect_uri’ parameter in order to redirect the user to the client application after successfully authenticating with the OAuth credentials. Then the access token or authorization code is submitted via the victim’s browser to the /callback endpoint specified in the ‘redirect_uri’ parameter. Since this parameter can be manipulated on the end-user side and a CSRF attack can be constructed, an attacker can trick the victim’s browser into initiating an OAuth flow that will send the code or token to an attacker-controlled ‘redirect_uri’. This will cause confidential code/token leakage.
Once the attacker becomes successful in stealing the authorization code, he can simply submit this code to the client application’s legitimate /callback endpoint to get access to the user’s account. Since this confidential URL will be leaked to the attacker, he doesn’t need to know any further information. The client application will simply complete the code/token exchange on the attacker’s behalf before logging them into the victim’s account.
Flawed redirect_uri validation
Since changing the redirect_uri
parameter can cause a huge security issue, proper validation must be done so that any malicious actor can’t simply change its value to point to an evil site. For this, it’s best practice for client applications to provide a list of genuine callback URIs when registering with the OAuth service. With this technique, the OAuth flow will only work as expected if the redirect_uri value is in the whitelist. Any tampering with this parameter will simply result in an OAuth error.
Even though the validation is being made, it may be weak resulting in bypasses. Some of these techniques may lead to redirect_uri validation bypass:
- Parser Discrepancies: Its good to experiment with this parameter value by adding/removing arbitrary paths, query parameters, and fragments to see what you can change without triggering an error. For example: https://default-host.com&@foo.evil-user.net#@bar.evil-user.net/
- HTTP Parameter Pollution: Polluting the URL by submitting the redirect_uri paramter twice. https://oauth-authorization-server.com/?client_id=123&redirect_uri=client-app.com/callback&redirect_uri=evil-user.net
- Localhost URIs: These URIs may get special treatment from the servers because they are mostly used during development. This may allow to bypass validation by registering a domain name such as localhost.evil-site.com.
Exploiting open redirect for stealing access tokens
One of the popular methods for stealing access tokens is by exploiting an open redirect vulnerability. In any way, if you manage to pass this token to the page containing an open redirect, then you may effectively extract the token from the query parameters or URL fragment depending upon the grant type. This can be done with the help of a malicious script present in the attacker-controlled domain to which the victim is redirected exploiting open-redirect.
Preventing above vulnerabilities
Both the OAuth Service Provider and client application need to implement rigorous validation of the key parameters in order to defeat OAuth authentication vulnerabilities. Since there is very little built-in protection in the OAuth specification, it’s up to the developers to make OAuth flow as secure as possible. Various security strategies need to be implemented by both the parties which are enlisted below:
For OAuth service developers
- Strict validation of redirect_uri: The service provider should mandate the client applications to register a whitelist of valid domains and byte-for-byte checking need to be implemented for the incoming values. Rather than using regexes for pattern matching, only complete and exact matching should be allowed.
- Anti-CSRF token: The service provider must enforce the use of ‘state’ parameter with its value set to randomized unguessable string. Moreover, this ‘state’ parameter should be tied to user’s session to prevent any kind of XSRF attacks. Thus this makes it much more difficult for an attacker to use any stolen authorization codes.
- Validation in resource server: This is the last resort of validation where checks must be performed to ensure everything is in place. To defend against scope upgrade attacks, strong checks should be performed so that the scope being requested is same as the scope for which the token was originally granted.
For client application developers
- Developers must have sound knowledge of OAuth mechanism before implementing it. Most issues creep in just because of simple lack of understanding of what exactly is happening at each stage and how this can potentially be exploited.
- Strict usage of ‘state’ parameter to prevent XSRF attacks.
- Send a redirect_uri parameter not only to the /authorization endpoint, but also to the /token endpoint. This way the access-token issuer can validate whether this matches the one it received in the initial authorization request and reject the exchange if not.
- In case of making authentication using OpenID Connect id_token, make sure it is properly validated according to the JSON Web Signature, JSON Web Encryption, and OpenID specifications.
- The access-tokens or authorization-codes may get leaked in the Referer header if not properly configured. This can be eliminated by setting the Referer Policy to strict-origin-while-cross-origin.
Reference: Portswigger Academy
Founder of cybersecnerds.com. Electronics Engineer by profession, Security Engineer by passion.
I am a Linux Enthusiast and highly interested in the offensive side of the CyberSec industry. You will find me reading InfoSec blogs most of the time.