Web Security — access token in url

Photo by Markus Spiske on Unsplash

The other day I was trying to understand potential security issues with exposing secrets or tokens in the url, and how OAuth implementations protect against that. Turns out, there are a few legitimate concerns, and a few patterns in OAuth that specify how to send tokens securely.

When the browser (or any other client) makes a request to a server, there are some intermediate servers which your request goes through, and they can see the URL, unless you use https. These can be any intermediate routers, CDNs, reverse proxies, etc. Some of these might be malicious and if they can read the url, they can read the secret in url as well (This would be similar to how a man-in-the-middle attack works, though in this case, we are just talking about being able to read the url, which is much easier then a full fledged MITM). At this point, it would be obvious how someone can misuse the tokens if they can read the url (they can impersonate you, the user).

That is because https (TLS) encrypts the urls and the rest of the data at the TCP layer and none of the intermediate servers see the actual url, so there is no way for any secrets leaking through the url. https://stackoverflow.com/questions/499591/are-https-urls-encrypted

OAuth protects against this by making sure that the access token is never passed in the URL (except for the implicit flow, the use case for which we will see later). As an example, consider the authorization code flow, where the client redirects the user to the auth server, and the auth server after validating the client credentials, redirects to the client app server with an auth code in the url, the client app server then exchanges this auth code for an access token. All of the exchange related to the access token happens through the back end, and never through the browser, so there isn’t a chance for the access token to leak through frontend attacks.

the app server exchanges the auth code for an access token, by passing in the client secret as well as the auth code using a POST call, which means the access token can’t be leaked through the url.

Also, this POST call happens to the auth server on an https connection, so MITM attack vectors are also mitigate. Notice how the onus is on the auth server to have a https connection, not on the client or the app server. This makes sure that the security for the access token is handled on the auth server side. The client app side https can be broken due to many reasons, so it is better to not be trusted in general.

Even if the client is http and not https, the auth server only returns the auth code to the client, which, even if leaked through url (or any other MITM attack), can not be used until the attacker also has the secret key.

Not really, URLs are still liable to be logged at the end server, which means the access token can still be leaked if an attacker was to get access to server logs.

This is still susceptible to shoulder-surfing.

The url also remains in the browser history, which is a potential vulnerability as well.

An exception: the implicit grant flow

The authorization code flow as discussed above, is only applicable if you have a server which has the client secret and can talk to the auth server to get the access token. The flow can’t work if the browser / client is the one who has to exchange the auth code for an access token, as then the client secret would need to be exposed to the browser, and any attack would lead to revealing both the auth code and the secret (and consequently the access token). It is NEVER safe to trust the browser with any secrets. But, there are some browser based applications which might not have a server, a single page app for example, how can they follow a similar flow?

There is an implicit flow specified in OAuth, where the access token is sent in the url, and hence the browser client can directly use the access token.

using the hash fragment.

The auth server appends the access token in the hash fragment of the return_uri provided by the client app, and after user login, responds to the browser with a 302 with a Location header which has the redirect uri + access token in hash fragment. The browser then redirects the user to the redirect_uri based on the Location header in the 302 response, with the access token in the hash fragment.

The hash fragment is only known to the browser and only processed by the browser, so when the browser sends a request to the redirect_uri, it does not send the hash fragment to the redirect_uri server, and hence none of the intermediate servers see the token in the url. The hash fragment has to be handled by the client javascript at the redirect_uri. https://stackoverflow.com/questions/25765619/how-exactly-hash-fragment-based-security-works

Since the access token is with the browser here, it is usually short lived anyway to mitigate other browser based attacks (and the refresh token is not supported in the implicit grant flow, as the browser can’t be trusted with any secrets to be stored)

What kind of tokens are still ok to be sent directly in the URL ?

You might have noticed that there are still some use cases, where you get the token in the url directly, like reset password emails. In those cases, the token is one time use, so leaking it through the url is not a significant danger.