8

I've been spending a lot of time looking into building an API that needs to be accessible by a single page JavaScript application and how to make it as secure as possible.

A lot of what I'm reading about standards like OAuth suggest that you never want the access token you issue to be made available directly to the client, and that's why things like the Implicit Grant are often criticized.

I recently read this article: http://alexbilbie.com/2014/11/oauth-and-javascript/

...which offered an interesting suggestion of proxying requests to the API through a thin server side component, so that the single page app could authenticate with the proxy, get back an encrypted session cookie, and then make requests to the proxy server which would attach the user's access token to the requests before forwarding them on to the actual API.

What I'm wondering is how this is any more secure than just handing the client an access token? If the attacker would've been able to get the access token, surely they can get the cookie storing the session information which is just as good as the access token for making requests to the API?

Why is it that we consider encrypted cookies with session details in them safe, but trusting the client with the actual access token is considered insecure?

Is the real difference just that an API with no direct client-side access is considered a secure, private API, and any API that needs to be accessed by the client side in any way (even through a proxy) needs to be treated as a public or open API, and you just have to accept the trade off of the risk of someone potentially being able to forge requests if they were able to get their hands on the session cookie or access token?

What is actually the most secure way for a single page application to prove it's identity to an API?

adam
  • 189
  • 2

4 Answers4

5

If the attacker would've been able to get the access token, surely they can get the cookie storing the session information

An XSS attack can't get to the cookie if you specify it with HttpOnly. It must have been an oversight, he really should have mentioned this. Oh, and Secure (HTTPS-only) as well. Then this cookie can't be stolen via XSS or MitM'd/sniffed.

Why is it that we consider encrypted cookies with session details in them safe,

So by putting the tokens in a cookie marked HttpOnly, they're safe from XSS. By encrypting them, you've prevented them from getting leaked, intentionally or not, for example by the user inspecting his cookies.

but trusting the client with the actual access token is considered insecure?

This problem isn't really the client but rather an XSS attacker, who could steal the access_token and then impersonate the user.

But now that we're using cookies, his last statement, made almost as an afterthough, is important:

To protect an attacker just stealing the cookie you can use CSRF protection measures.

To mitigate CSRF, you can enforce on the server that the client provide an X-Requested-With (or other custom) header. That will prevent malicious sites or XSS-hacked sites from calling your APIs because the browser's cross-domain restrictions won't let them add custom headers, and then you can reject the requests.

What is actually the most secure way for a single page application to prove it's identity to an API?

I've been looking around and I think this approach, using a proxy server-side, is it. You'll find variants of it elsewhere googling around, for example here and here, but Alex Bilbie's approach of encrypting the access_token has an advantage over those two that there is never a window (even a short one) where the access_token is vulnerable to XSS.

Steve Kehlet
  • 151
  • 1
  • 4
  • This. To learn more about session cookies: http://en.wikipedia.org/wiki/HTTP_cookie#Session_cookie And a little lower on the same page is the HttpOnly and HttpsOnly cookie, which is a flag for session cookies that can prevent javascript from reading them. So an Https Session cookie is an encrypted cookie that only the browser can access, and is very secure. The only way to compromise it is to hack the client's browser. – Andrew Hoffman Mar 16 '15 at 20:17
2

The problem is that access tokens are not bound to the client. Hence there is no way to know if an access token was issued for your client or for another client.

Basically, from OAuth 2.0 point of view, whoever own the access token is YOU.

I wrote a big answer explaining how this is a problem. Here is the attack you can mount :

Let's say your REST service required an access token from facebook. All an attacker need to do is to host a service, for example stackoverflow, and require an access token from facebook. When you give the facebook access token to stackoverflow, stackoverflow (our attacker) can now impersonate you with your REST service.

All that because access tokens are not bound to a specific client.

It might sounds crazy that OAuth allow something like that but it's true. OAuth just assume that the risk is acceptable because it was meant only for authorization and not authentication. And that's the next big issue. Most people don't know the difference between authorization and authentication and are misusing OAuth left and right.

Part of the confusion comes from the fact that the Authorization Code flow also provide authentication as a side effect... I suggest you read the following section in OAuth specification :

What is actually the most secure way for a single page application to prove it's identity to an API?

If you want authentication, use OpenID. OpenID was meant for authentication. OAuth is only authorization.

OpenID is built on top of OAuth and add all the missing pieces that are required to provide authentication. One of them is that contrary to OAuth, OpenID bind the access token to the client so the attack mentioned above no longer works if you are using OpenID.

Gudradain
  • 6,921
  • 2
  • 26
  • 43
  • It may be good to specify that you speak about OAuth 2.0, because OAuth 1.0a provides both authentication and authorization. – luben Mar 05 '17 at 11:15
  • @middlehut Added. I didn't expect anyone to be confuse about that. OAuth 1.0a is obsoleted by version 2.0 and practically every OAuth provider out there use the version 2.0. – Gudradain Mar 06 '17 at 14:06
  • 1
    Twitter still uses OAuth 1.0a. – luben Mar 06 '17 at 15:33
1

Why is it that we consider encrypted cookies with session details in them safe, but trusting the client with the actual access token is considered insecure?

Another key element from Alex Bilbie's article is that you can't do encryption on the client (in javascript) because you can't keep the decryption key safe - which means that anyone could obtain the key and decrypt encrypted stuff.

By doing the encryption server side, the secret key is never exposed to the client, who merely has an 'opaque' token to offer up to services.

Apart from that addition, I completely agree with Steve Kehlet.

Alex White
  • 211
  • 3
  • 4
1

I think that what he is trying to say is that you should not keep all the keys in the same place. The compartmentalization of the process is what plays the key role in the auth mechanism he suggests.

One thing that you should also keep note is to take benefit from the Same Origin Policy. Using SOP will prevent attackers from manipulating data from a domain that is different from the one the got access to (unless he/she exploits the browser). And do not use WebSockets to manipulate sensitive information, they are not subject to this policy.

Talking about the current standard (if that is what you mean for "most secure way") for doing this, i'll let that to the developers here...

DarkLighting
  • 1,523
  • 11
  • 16