How to Identify OAuth2 Vulnerabilities and Mitigate Risks
OAuth2 Case Studies based on HackerOne Public Disclosure Reports
Even though OAuth2 has been the industry-standard authorization framework since it replaced OAuth1 in 2012, its many complexities have led to potential security issues. For security engineers, it's vital to understand what OAuth2 is, how it works, and how poor implementation can lead to vulnerabilities.
In this article, we'll highlight some common attack vectors or vulnerabilities against OAuth2 by referring to HackerOne public disclosure reports with actual cases — and explain how to mitigate those.
What is OAuth2?
OAuth2 is a widely used framework for access delegation, which allows users to grant limited access to one application (client application) by requesting the resource of the users hosted in another application or website (resource application).
If you have some basic knowledge about OAuth2 but have never implemented OAuth2 in your application, the two diagrams below might help you to refresh the concepts of OAuth2 and the mechanism of how it works. The diagrams illustrate the workflow for two common OAuth2 grant types, authorization code grant (Figure 1) and the still-in-use but deemed insecure implicit grant (Figure 2).
OAuth2 itself is fundamentally complicated as it is designed to resolve the vital authentication part in many complex web environments (mobile app, web server, etc). Due to the complexity, many security engineers may not fully understand the power of OAuth2. As a consequence, we are observing many security issues caused by a misconfiguration or poor implementation of OAuth2. To make it worse, some exploitations against these OAuth2 misconfigurations are extremely simple and easy to launch.
Common OAuth2 Vulnerabilities from HackerOne’s Public Disclosure
Let’s take a look at some common attack vectors or vulnerabilities against OAuth2 by referring to HackerOne public disclosure reports. We hope the explanations of these vulnerabilities are clear by making reference to the actual exploitation disclosure. At the end of each of the following sections, you will also learn how to mitigate these vulnerabilities.
Vulnerability 1: Missing validation in redirect_uri leads to access token takeover
The redirect_uri parameter in the OAuth2 workflow is used by the authorization server as a location or address to deliver the access_token or auth_code by means of a browser redirect. In Figure 1, we described that the redirect_uri parameter is initialized by the client application as part of the request to the authorization server under step 2 when a web user clicks the login button. After the authorization server validates the credentials (step 6), it will send back the auth_token (or access_token for an implicit grant step 7 in Figure 2) as a parameter to the redirect_uri used in step 2.
If a malicious user could trigger the victim to send a request to the authorization server with a redirect_uri controlled by the attacker and the authorization server is NOT validating the redirect_uri, the access_token will be sent to the URI controlled by the attacker.
The case of stealing users’ OAuth tokens via redirect_uri is, unfortunately, a typical one, where the authorization server performs a poor validation on the redirect_uri and the attacker is able to bypass the validation with a malicious link they control.
Implement a robust redirect_uri validation on the authorization server by considering the following approach:
- Perform a match between client_id and report_uri to ensure the report_uri matches with the client_id stored in the authorization server.
- Use a whitelist approach if the number of client applications is manageable.
Vulnerability 2: Missing state parameter validation leads to CSRF attack
In OAuth2 implementation, the state parameter (initialized under step 2) allows client applications to restore the previous state of the user. The state parameter preserves some state object set by the client in the authorization request and makes it available to the client in the response.
Here is the correct implementation of the state parameter:
- The client application initialized the request to the authorization server with a state parameter in the request URL (Step 2).
- The client application stores the state parameter value in the current session (Step 2).
- The authorization server sends the access_token back to the client application (Step 7 in Figure 2) together with a state parameter.
- Client application performs a match between the state stored in the current session and the state parameter sent back from the authorization server. If matching, the access_token will be consumed by the client application. Otherwise, it will be discarded so that it could prevent the CSRF attack.
However, since the state parameter is not required for a successful OAuth2 workflow, it is very often this parameter is omitted or ignored during OAuth2 implementation. Without validation on the state parameter, CSRF attack could be launched easily against the client application.
This HackerOne report is a very good example to explain how an attacker could attach their account to a different account under the client application due to the lack of the state parameter. Sometimes, even the state parameter is present in the callback request from the authorization server, but it is still possible the state parameter is not validated, leaving the application vulnerable to CSRF attack.
Ensure the state parameter is passed between requests and state validation is implemented so that an attacker could not attach their account to the victim’s account.
Vulnerability 3: Client_secret mistakenly disclosed to the public
The client_secret is used by the client application to make a request to the authorization server to exchange the auth code to the access token (step 8 in Figure 1). The client_secret is a secret known only to the client application and the authorization server.
To avoid disclosing client_secret to the public, it is best for developers to understand the need of implementing OAuth2, as there are different OAuth2 options to adopt for different applications. If your client application has a back-end server, the client_secret should never be exposed to the public, as the interaction with the authorization server could be completed in a back-end channel. If your client application is a single-page web application or mobile app, you should choose a different OAuth2 type. For example, use the Authorization Code grant with PKCE instead.
Vulnerability 4: Pre-account takeover
A pre-account takeover could occur when the following two conditions are met:
- The client application supports multiple authentication methods, using a login with a password and a third-party service (like Facebook or Google) as an OAuth authentication provider.
- Either the client application or the third-party service does not perform email verification during the signup process.
This HackerOne report details how a misconfigured OAuth can lead to pre-account takeover:
- Attacker creates an account with a victim’s email address and the attacker’s password before the victim has registered on the client application.
- The victim then logs in through a third-party service, like Google or Facebook.
- The victim performs some sensitive actions in the client application. The client application will save these actions, and it will probably use the email address as an identifier for its users in the database.
- Now, the attacker could log in as the victim and read the sensitive data added by the victim by using the victim’s email address and the attacker’s password created by step 1.
Perform email validation when creating a new user.
Vulnerability 5: OAuth2 access_token is leaked through referrer header
One weak design of OAuth2 itself is that it passes the access_token in the URL for implicit grant type. Once you put sensitive data in a URI, you risk exposing this data to third-party applications. This applies to OAuth2 implementation as well.
In this HackerOne report about access_token smuggling, for example, the access_token was exposed to a third-party website controlled by the attacker after a chained redirection by taking advantage of the referrer header.
As this is a design issue of OAuth2, the easiest mitigation method would be strengthening the referrer header policy with <meta name="referrer" content="origin" />.
Vulnerability 6: OAuth2 login bypass due to lack of access_token validation
A lack of access_token validation by the client application makes it possible for an attacker to log in to other users' accounts without knowing their password.
Once the authorization server sends the access_token back to the client application, client applications sometimes need to bind the access_token with a user identity so that it can store it as a session. The exploitation of this vulnerability happens when an attacker binds their access_token with any user identity and then impersonates that user without logging in.
In this HackerOne report, the security researcher was able to log in as any user just by supplying the victim’s email address only because the client application did not validate whether the access_token belongs to the correct owner.
Validation should be performed on the client side to check whether the user owns the access_token.
The OAuth2 framework is complicated and provides many flexibilities for implementation. However, due to this flexibility, the security of OAuth2 implementation is in the hands of the developers. With that said, developers with a strong security mindset can make implementation more secure; on the contrary, developers with less security training are likely to impose some security holes during OAuth2 implementation. For any organization, it’s vital to train and educate your developers with the latest security best practices to reduce risk during OAuth2 implementation.
At Coupa, our engineers are committed to following the latest OAuth2 security best practices to make sure our OAuth2 implementation is secure.