9

I've seen the answer Is CSRF possible if I don't even use cookies? but there are 2 conflicting answers and the question itself doesn't provide that much information either.

I am creating a REST API that will be used by a web client (of our own creation) running on another domain, so we will be doing CORS requests. This API runs as an oauth2 resource server, so access is restricted by access tokens which are passed in the authentication header. We do not have any cookies there, everything is stateless. I am following the advice in the article at https://spring.io/blog/2011/11/30/cross-site-request-forgery-and-oauth2

  • We will use SSL
  • We have pre-registered redirect uri's
  • Our only grant type is authorization code, without requiring a client secret (as the web client source is public) instead of the implicit grant type, as per the recommendations on https://oauth.net/2/grant-types/implicit/
  • The client authenticates with a header when it does actual resource server calls
  • Obtaining the access token is done by passing the 1-time authorization code in form parameters (x-www-form-urlencoded), which seems to be the normal way, shouldn't get stuck in browser history as it's not a query parameter.
  • The client will also use some secret state parameter when getting the authorization code. In this case I'm not sure there is a point since the client does not depend on a secret to get an access token by exchanging a code anyway.
  • For the oauth/token endpoint where you actually get an access token, the only allowed origin is the domain of our web client
  • For the oauth/authorize endpoint, CORS is not allowed. Perhaps this is excessive since the redirect uri would only point back at our own domain anyway?

I think that covers pretty much everything on that side.

Now there IS another possible attack vector in the form of the oauth2 authentication server itself, which is supposed to be SSO and does have sessions and is accessible with basic auth. However, there do not seem to be any actions you could take with a csrf attack that would actually be useful. You could do a request to these oauth endpoints but you would have to be able to read the result, which is prevented by the CORS configuration and the fact the redirect uri's are pre-registered. There is no POST you can do there to change your password, reset your email address, et cetera, all those things are actually also exposed as a resource server.

Am I missing anything? How would you go about probing for vulnerabilities here?

1 Answers1

10

To answer your initial question: you do not need to implement CSRF counter-measures on your resource server, if you are not using cookies (sessions) and you are not using basic authentication within the browser.

Cookies and the Basic authorization header are stored by the browser for every domain name you visit, and every time you send a request to a site, the saved cookies and Basic authorization header are automatically added to the request headers.

Since you are using an access token, and presumably sending it using Authorization: Bearer <token>, you should be secure. Your browser does not know how to handle this kind of authorization and that is also why you have to manually add this header in every request.

Redirect URIs

You are using pre-registered redirect URIs, which is recommended by the OAuth 2.0 standard. This ensures that your authorization server only sends authorization codes to sites that you trust.

It is important that your redirect URIs are using a secure scheme, such as HTTPS, otherwise you are vulnerable to a MITM attack.

Exchanging authorization code for access token

Your site is receiving an authorization code through a query parameter, when you are redirected back from the authorization server. Most likely, you are immediately exchanging the authorization code for an access token, which should revoke the authorization code afterwards.

The state parameter should be used here to prevent CSRF attacks, e.g. by sending a random value to the authorize endpoint, and checking that you are receiving the same value with the authorization code. In a SPA, you could save the random value in local storage i.e.

In a SPA, you have to worry less about this, than e.g. in a stateful server-side web application. What happens, if an attacker redirects you to the authorize page, from his own web page? You end up in your own SPA, where the attacker should not be able to continue.

In a stateful server-side application, it's possible that by doing this authorization, you will actually link accounts together, and things can start happening. (Depending on the application, of course.)

CORS on token endpoint

First off, CORS is only checked by your browser, so you must not rely on it to stop an attacker from using your token endpoint.

To circumvent CORS here, all an attacker would have to do, is to create a proxy endpoint, which just sends the request to your token endpoint.

Moreover, the token endpoint only works if it receives a valid authorization code.

CORS on the authorize endpoint

Normally, you would redirect your users to the authorize endpoint, where they would (sign in and) click an authorize button, granting access to the application.

Without CORS, a likely attack is that an attacker can create his own site, where he uses XHR/fetch to retrieve your authorize page. And assuming that the user is signed in (statefully) on your authorize page, he would be able to click the authorize button, without the user's consent.

There is no scenario where it is necessary to use XHR/fetch to retrieve the authorize page. So you can safely block it with CORS.

Authorization server

If your authorization server is stateful, as you write, then you should implement CSRF protection whenever you perform an action that can change something. It's best practice to do this in general, and if you add an update password function in the future, you already have the necessary code to protect against CSRF.

Recommendations

It sounds like you understand the OAuth spec. and it looks like you are doing what you are supposed to.

  • The obvious recommendations are to use HSTS with preloading (and HPKP), to prevent MITM attacks.
  • Ensure that your authorization codes are short-lived and can be used only once. They do not need to be valid for very long, in normal scenarios it is used immediately after being granted.
  • Do not store final access tokens, refresh tokens, authorization codes in your database. Instead store an identifier (e.g. 64 bytes of random), and issue a signed version (e.g. JWT). This prevents attackers from extracting access tokens from your database, since the attacker cannot use it anyway.
  • Use a standard implementation of OAuth, don't roll your own.
Daniel
  • 351
  • 2
  • 9
  • Thanks, sounds good, I'm doing the vast majority of this, the access tokens etc are just stored in memory so no big deal either. Would like to move to JWT later too but it's not prioritized right now. And changing a password is exposed as a resource server (albeit on the same application as where the oauth endpoints live), only the part where you can get the authorization code is stateful with http basic auth. – Sebastiaan van den Broek Jul 24 '18 at 07:14
  • If you are prompting the user whether they want to authorize the application, you should implement CSRF on that page. If you consider the application as trusted, and immediately redirect back with an authorization code, then there is no need for CSRF, since there is no POST request. – Daniel Jul 24 '18 at 09:02
  • Yeah we don't have a prompt so that should be fine, thanks. – Sebastiaan van den Broek Jul 25 '18 at 10:00