Secure OAuth 2.0: What Could Possibly Go Wrong?
The previous section, Starting with OAuth 2 — Security check, covered the main threats which are the users’ sensitive data leakage and the account takeover. In this section I will focus on the different types of insecure implementation that can lead to those attacks.
OAuth used for authentication
One of the most common mistakes in OAuth implementation is the design decision to use it as an authentication mechanism. The official OAuth web page stays: OAuth 2.0 is not an authentication protocol.
The authentication process is used by the application to confirm who the user is and whether he is present. A full authentication protocol will probably also tell the application a number of attributes about this user, such as a unique identifier, an email address, and what to call them.
OAuth tells the application none of the above. The assumption that possession of a valid access token is enough to prove that a user is authenticated and present is true only in some cases (when the access token was freshly minted). Moreover, there are other ways to obtain valid access tokens than authenticating resource owners. For example, using the refresh token.
Authentication bypass in hybrid OAuth 2.0 implementation
In the first part I have described a so-called hybrid implementation. The application is both the data provider and data consumer at the same time. Here is a story of such an application and its interesting vulnerability in Single Sign-On (SSO) authentication mechanism based on OAuth 2.0.
- The SSO mechanism allowed users to log in using accounts from Active Directory.
- However, a few third-party applications, integrated with this mechanism, additionally allowed users to log in using Google accounts. In that case, the button to log in with a Google account was added on the login page. On the other hand, when the user was redirected from another application, the button did not show up (user was allowed to log in with Active Directory only).
- The third-party application that accepted Google account either verified whether the logged in e-mail address is accepted (there was a list of accepted Google email addresses) or simply allowed anyone (any Google email address) to have a valid account.
The vulnerability appeared because the other group of third-party applications were not aware of the fact that users can log in to SSO with Google accounts as well. They did not verify whether the authorization code, that was returned to them with redirection, came from the login process initiated by them. They just used the code to get the access token.
The attack scenario is the following (from the attacker’s perspective):
- Start the login process for the third-party applications that accepts Google accounts.
- Login in to SSO using any Google account.
- Switch the context of the login process to another application that accepts users only from Active Directory and provides it the valid code from SSO.
- The attacked application generates valid token from the code and lets the attacker in.
Long story short, the attacker could log in using any Google mail to the third-party application that allowed accounts from Active Directory only.
Insecure grant type
OAuth supports different grant types for obtaining access tokens. One of them is implicit grant type where the token is passed directly as a parameter in the URL hash. This type was created for Single Page Applications that were not allowed to send requests to different hosts (due to the Same Origin Policy) to get the access token when using a safer grant type — the authorization code type.
In the draft of OAuth 2.0 Security Best Current Practice that was published in 2018 the authors proposed that clients should not use implicit type (“or any other response type causing the authorization server to issue an access token in the authorization response”) and use authorization code type instead.
What about the Same Origin Policy limitation that did not allow to send requests to different hosts?
Today we have CORS (Cross-Origin Resource Sharing) and the server can allow other origins (e.g. domains) to send requests.
The justification for that statement was the existence of multiple threats, like Credential Leakage via Referrer Headers, Attacks through the Browser History or Access Token Injection and “no viable mechanism to cryptographically bind access tokens issued in the authorization response to a certain client”.
The injection threat comes from the fact that third-party applications cannot assume that only the resource owner can present it with a valid access token for the resource.
Therefore, an adversary can easily inject the leaked, stolen or forged access token (and impersonate the resource owner) when a third-party application accepts access tokens from sources other than the return call from the token endpoint. That happens when a client uses an implicit flow.
The OAuth authorization process starts in a third-party application which asks the user for permissions to get his personal information which is kept on the resource server. User is redirected to the authorization server which presents what information is going to be shared with a third-party application. When the user accepts the request and confirms the permissions he is redirected back to the third-party applications together with the access token or the authorization code. The redirection URL is specified in the first request from application.
Do you see the potential risk? What if the authorization server allowed any URL for redirection?
Exactly! An attacker could send a link with a faked redirection URL to the victim. After the victim logs in to the authorization server, he is redirected to the URL controlled by an attacker and the access token or the code is leaked.
Actually, this is a very popular vulnerability in OAuth 2.0 and even big players are vulnerable to open redirection.
In 2016 an open redirection vulnerability was found in PayPal website. They did not allow any redirection URL but the validation function was implemented incorrectly. It allowed any redirection URL that started with a third-party application domain (e.g. company.com). The problem was that they accepted⚠️ any ⚠️domain that started with company.com, so attackers could create the company.com.attacker.com domain and steal access tokens.
Cross Site Request Forgery
The threat worth mentioning, which is actually independent from the grant type, is the Cross Site Request Forgery (CSRF). This attack involves an attacker tricking a victim into clicking a link that changes some data on the resource server. If the victim is already authenticated he might not even notice the attack since the browser will send authentication headers or cookies automatically.
In case of OAuth 2.0 the victim can be tricked to click a link that would fake the last step of the authorization process in which the victim is redirected from the authorization server back to the third-party application either with access token or with the authorization code.
What are the consequences?
If the application does not verify whether the redirection comes from the authorization process initiated by the application it may accept it and change the access token to the one owned by the attacker. So, if you do not protect your OAuth implementation from CSRF, the attacker can return fake data from API to your users and sniff the data that users send to API.
What to do next?
That was the second part of our Secure OAuth 2.0 Implementation series. We have covered the potential consequences of security bugs. In the third part, which is the last one, we will point out how to implement OAuth 2.0 securely.