# Mitigating OAuth’s recently discovered Open Response Type vulnerability

On July 29, security researchers at Salt [released details](https://salt.security/blog/over-1-million-websites-are-at-risk-of-sensitive-information-leakage---xss-is-dead-long-live-xss)
of an OAuth vulnerability that can reliably be chained with any XSS vulnerability
to establish a long-lived account takeover. This is a general OAuth vulnerability, not particular to Clerk’s implementation.

At Clerk, we believe it’s our responsibility to protect our customers from vulnerabilities
like this, so we worked quickly to understand the attack and devise a mitigation.
Fortunately, we discovered that **Clerk’s default configuration is not susceptible to this
chaining, and over 99.7% of our customers were already protected.** For the last 0.3%, we
released an update today that mitigates the attack.

In this post, we dive into the technical details of the vulnerability, why most customers were protected by default, and how we mitigated the attack for the last few.

## Understanding the Open Response Type vulnerability: a clever variation of the Open Redirect

### The OAuth happy path

The OAuth flow starts by redirecting a user to the “authorization URL” of an identity
provider, with `redirect_uri` and `response_type` in the query param, as well as an application identifier and the scope of authorization requested. Here’s a sample for Google:

```plaintext
https://accounts.google.com/o/oauth2/auth
  ?redirect_uri=https%3A%2F%2Fmyapp.com%2Foauth_callback
  &response_type=code
  &client_id={oauth_client_id}
  &scope={requested_authorization_scopes}
```

On Google’s website, the user is asked to authorize the requested scope, which
normally only includes profile information so the user can be signedin.

After authorization, the user is redirected back to the `redirect_uri` using
the given `response_type`. In this case, the response type is `code`, which means
a single-use secret code is appended to the `redirect_uri` as a query param:

```
https://myapp.com/oauth_callback?code={secret_code}
```

### The Open Redirect vulnerability

An “Open Redirect” is when an attacker tricks a user into visiting an
authorization URL with a nefarious value passed to `redirect_uri`.
For example, instead of passing `https://myapp.com/oauth_callback`,
they can pass `https://attackapp.com/oauth_callback`. This accomplishes two things:

1. `https://myapp.com/oauth_callback` doesn’t receive the secret code,
   so its single-use nature does not provide any protection.
2. The attacker gets access to a secret code.  With it, they can open
   a new browser on their own machine, and navigate to the valid `redirect_uri`
   with the code appended. This tricks the application into granting a
   session for the victim to the attacker.

Thankfully, the OAuth specification guides implmentors on how to prevent Open Redirects, and Google and
other identity providers maintain a strict allowlist of acceptable `redirect_uri`s. The allowlist ensures that
the user, _and the secret code,_ will not be sent to a URL the attacker controls.

### The Open Response Type vulnerability

In OAuth, the response type dictates the format and mechanism for how
the identity provider passes sensitive information back to the application.
With `code`, a single-use token is passed by query param.
With `token`, an access token is passed by the URL’s hash fragment.

Unlike `redirect_uri`s, the `response_type` parameter is not strictly
bound to a per-application allowlist. This lack of enforcement is pivotal
to the attack, which is why we’re calling it the “Open Response Type”
vulnerability. Here is [Salt’s explanation](https://salt.security/blog/over-1-million-websites-are-at-risk-of-sensitive-information-leakage---xss-is-dead-long-live-xss) of how the parameter was leveraged:

> Note that we changed the original OAuth link to Google - we used the response
> type “code,token” instead of only “code”, which makes Google send the code in
> the hash fragment (#code=...). This enables us to read the code from the URL
> and ensure Hotjar doesn’t consume the code, which can be consumed only once.

After changing the response type to `code,token`, the secret code is returned
in the fragment instead of the query param. In all likelihood, the application
implementing OAuth has not prepared for a secret code to be in the fragment,
so it doesn’t consume it, and it remains in the URL instead.

Put another way: the open nature of `response_type` means an attacker has a
reliable way to get a valid, unused secret code in the URL bar.

Thankfully, getting the code in the URL bar alone is not sufficient, since the
attacker also must be able to read its value. This is not normally possible,
but it is when an XSS vulnerability is present. Depending on the page the XSS
is present on, it may be as easy as reading `window.location.href`.
In Salt’s case, they opened a new tab to the Authorization URL via `window.open`,
then polled the tab’s `location` until the OAuth flow completed.

### The impact

You might be wondering: **Do we really need to worry about Open Response Type
if it needs to be chained with an XSS attack?**

The answer is **absolutely yes**. When managing user sessions on the web, it’s
critical to mitigate the effects of XSS as much as possible. After an XSS is
patched, it should be impossible for the attacker to continue acting on behalf
of any users.

This is why developers use the `HttpOnly` directive when managing session cookies:
it ensures that session tokens cannot be extracted for the attacker to continue
using after the XSS is patched. Without this feature, developers would need to
revoke sessions after an XSS attack and force users to sign in again.

The challenge with the Open Response Type vulnerability is that it completely
bypasses the protections afforded by `HttpOnly`. The attacker is able to generate
new sessions, and those sessions will continue to be valid after the XSS is patched.

## Mitigating the Open Response Type vulnerability

The Open Response Type vulnerability relies on two behaviors:

1. The user must land on a page with an unused code in the URL bar.
2. The application must have an open XSS vulnerability that allows them to
   programmatically read the URL bar.

Below, we list two techniques that can be used to suppress these behaviors.

### Mitigation 1: Process the OAuth code on a separate origin

At the top of this post, we mentioned that over 99.7% of Clerk customers
were protected by default. That is because Clerk forces developers to set
the OAuth `redirect_uri` to be an endpoint on our API, which is normally
hosted on a subdomain like `https://clerk.yourdomain.com`. When the Open
Response Type attack is attempted against a Clerk customer, the user would
land on URL on this subdomain, like:

```
https://clerk.yourdomain.com/v1/oauth_callback#code={secret_code}
```

Fortunately, this subdomain does not serve HTML, so it’s exceedingly unlikely
to be the source of an XSS. An XSS on any other subdomain cannot read the URL
of a tab on the Clerk subdomain, since it breaks the web’s cross-origin rules.

If your application also leverages a separate origin for processing the OAuth
code (e.g. `identity.yourdomain.com` or `auth.yourdomain.com`), it will also
be protected as long as that origin never introduces an XSS.

While this technique helps, it’s hard to guarantee that you will never introduce
an XSS on the origin processing the OAuth code. For that reason, we recommend
using Mitigation 2 instead.

### Mitigation 2: Eliminate unexpected fragments

Some Clerk customers use a reverse proxy to host our API directly on their
primary domain, so OAuth codes are not processed on a separate origin. For
these users, and for a more robust solution overall, we needed to find an
additional mitigation.

Our solution is to eliminate unexpected fragments from the URL bar before
an XSS vulnerability has the chance to read them. To do this, we’ve added an extra redirect to any OAuth failure that eliminates
the fragment.

Again, let’s consider a user that lands on this URL as a result of an Open
Response Type attack:

```
https://clerk.yourdomain.com/v1/oauth_callback#code={secret_code}
```

Fragments aren’t sent to the server, so our server cannot tell that `code`
is even present. But, this URL is expecting `code` in the query param, and
since it’s missing, Clerk is going to return an error.

Instead of simply returning the error, we are now issuing a redirect first.
We issue the redirect with status=301 and a Location header that has a
trailing #, which clears any fragment that might exist:

```
Location: https://clerk.yourdomain.com/v1/oauth_callback#
```

Since we use Clerk at Clerk, you can confirm the behavior by visiting the
URL on our domain with a fake code – the code will be stripped away.
(You’ll notice we add an extra query param to prevent an infinite redirect.)

```
https://clerk.clerk.com/v1/oauth_callback#code=fakerandomcode
```

This behavior is easy to implement in your own OAuth handler and will
effectively protect you against Open Response Type attacks.
