5

I'm currently trying to set up an OAuth 2.0 authentication scheme to protect some stateless REST webservices. These services deal with sensitive information, and the OAuth support is a requirement.


The issue is that the client application is 100% on the client side (Angular/Javascript), and there are no user sessions at all, so I'm struggling with how to deal with a token on the client-side without exposing it to attackers.



The plan is to do something like:

  1. use user/password grant to get a token from the IdentityServer (client application is ours, and no client_secret is left laying around)
  2. store the token in a cookie
  3. when making a request, take the token from the cookie and use it to sign the request
  4. when the token expires, use the refresh_token to get a new one


To send the request, I figured I can either:

  • Send the HttpOnly cookie as authentication, instead of signing the token
  • Use Angular’s XSRF-TOKEN cookie, which means the cookie should only be accessible to Javascript running in our domain (though I believe this leaves still leaves it open to XSS attacks?)


The cookies will be all set to secure, and send over HTTPS. However, this still seems insecure, since the token is still stored as plaintext. The refresh_token will also be in the cookie. (I’ve considered local storage as well, but unless I missed something, it did not seem any safer...)



Is there a safer way to store an oauth token on the client-side?
Or is this acceptable security? What am I missing here?



Reference:

Angular XSRF protection: https://docs.angularjs.org/api/ng/service/$http#cross-site-request-forgery-xsrf-protection

lv.
  • 465
  • 1
  • 5
  • 10

1 Answers1

3

You are not worrying about the right things

Protecting information located on the client?

You don't need to do anything once the information has reached your client (ex.: your browser) in order to protect it. You could store the access token in a cookie, in a hidden field on your webpage, in html5 local cache or plainly visible directly in the middle of the page and it doesn't change anything (except shoulder surfing...).

Worrying about the access token once it's on the client is like opening notepad to write your email password and then worry that an attacker might be able to steal that information from a remote location. It doesn't happen. Unless your computer is already compromised but at this point you have already lost.

Where it makes sense to worry?

Usually, your information is vulnerable when it is in transit. In the case of OAuth2 (implicit flow), the access token will be in transit at two places :

  • from the authorization server to your browser
  • from your browser to the resource server

Protecting the information while it is in transit is as easy as using TLS everywhere. Which you should already be doing since you are using OAuth2 and it's required by the protocol.

Now the real problem

The way you intend to use OAuth2 is most likely not the way that you should be using it.

To understand why you will probably misuse OAuth2, you have to know about the flows. OAuth2 define 4 authorization flow

  • Authorization Code (only good one but... keep reading)
  • Implicit (false sense of security)
  • Resource Owner Password Credentials (horrible idea)
  • Client Credentials (not applicable to your case)

Since you using a javascript client, the only flow that works for you is the implicit flow and now starts the issues.

Implicit flow problems

There are many but let's just talk about the most critical one. Access token are not bound to a specific client! From the specification section 10.16 :

For public clients using implicit flows, this specification does not provide any method for the client to determine what client an access token was issued to.

This open the doors for attacker to impersonate you, the resource owner, and then gain access to the resource server. Let's keep reading section 10.16 :

A resource owner may willingly delegate access to a resource by granting an access token to an attacker's malicious client. This may be due to phishing or some other pretext. An attacker may also steal a token via some other mechanism. An attacker may then attempt to impersonate the resource owner by providing the access token to a legitimate public client.

In the implicit flow (response_type=token), the attacker can easily switch the token in the response from the authorization server, replacing the real access token with the one previously issued to the attacker.

Servers communicating with native applications that rely on being passed an access token in the back channel to identify the user of the client may be similarly compromised by an attacker creating a compromised application that can inject arbitrary stolen access tokens.

Any public client that makes the assumption that only the resource owner can present it with a valid access token for the resource is vulnerable to this type of attack.

That first attack is actually not even an attack but rather just a "flaw" in the implicit flow...

The next attack

Now starts the big troubles. You seem to be trying to use OAuth2 implicit flow as a form of delegated end-user authentication which it is not meant to provide. Back to the specification section 10.16

Authenticating resource owners to clients is out of scope for this specification. Any specification that uses the authorization process as a form of delegated end-user authentication to the client (e.g., third-party sign-in service) MUST NOT use the implicit flow without additional security mechanisms that would enable the client to determine if the access token was issued for its use (e.g., audience-restricting the access token).

At this point it's mostly game over for you.

How to mount that attack?

It's pretty simple. 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.

A solution

Don't use the implicit flow and instead use the authorization code flow. Which means that your 100% client side app will need to no longer be a 100% client side app.

Why are you not using the server that is serving the angularjs client to your user to handle the OAuth2 flow?

Reference : https://www.rfc-editor.org/rfc/rfc6749

Gudradain
  • 6,921
  • 2
  • 26
  • 43
  • Thanks so much for the great explanation. I was worried about protecting info from the client side because, if I understood correctly, if the token is stolen through XSS or some other form of attack, then it's trivial for an attacker to impersonate the user. When you put it that way, it's obvious that implicit will not do then. Why is the resource_owner grant so bad though? As long as the client is trusted, and the credentials are passed through HTTPS at least? I should have specified that there will be no real 'public clients' - all clients will be created by us or other trusted parties. – lv. Oct 16 '15 at 13:24
  • As for the server, it cannot be used, since there are no sessions, it will be a single-page webapp, and no processing should be done on that side (not my call, as much as I'd like to do otherwise, it's a constraint I'll just have to work with unfortunately). – lv. Oct 16 '15 at 13:24
  • @lv. Who is the authorization server? Is it something that you control? – Gudradain Oct 16 '15 at 14:29
  • Yes, it's an identity server (WSO2) deployed by us. – lv. Oct 16 '15 at 15:16
  • @lv. WSO2? Does it means that you control the source code or you just host a library that you did not develop? – Gudradain Oct 16 '15 at 15:21
  • @lv. What you want is not really a part of the OAuth2 specification so it's up to the implementation to add the missing parts... – Gudradain Oct 16 '15 at 15:25
  • Well, it's a full-fledged identity server (open-source), the OAuth specification support comes out-of-the-box. We simply host it and register our own clients. Not developed by us, but otherwise we control every aspect of it, which is why I assumed resource-owner would be okay enough security-wise... – lv. Oct 16 '15 at 15:39
  • 1
    @lv. Well, I don't know if it's your case, but if you are in fact using OpenID Connect, which is built on top of OAuth2, the answer might be different but I don't know the details about OpenID connect. OAuth2 is meant for authorization but some people figured it could be used for authentication in some case. OpenID connect is meant for authentication which is what you want. OpenID Connect might have those missing parts and WSO2 seems to use OpenID connect. You could try asking another question. – Gudradain Oct 16 '15 at 15:53
  • ... actually, I was using bare-bones OAuth, but you're right, OpenID seems a much better fit to my problem - authentication vs authorization indeed. – lv. Oct 16 '15 at 16:23