CSRF Protection for Refresh Token Cookie in SPA

2019-02-09 15:20发布

问题:

I am using the Resource Owner Password Credentials OAuth 2.0 flow in a AngularJS SPA. There are several articles (here, here..) and the answer to this question that explain that we should not store refresh tokens on the (web) client (LocalStorage), but store them encrypted in an HttpOnly Cookie and use a proxy API where we implement the decryption of the refreh token to forward it to the security token service.

Most articles give a hint that we should care about CSRF by using one of the common protection mechanisms. I'm wondering what's the best solution in a Single Page Application.

The Angular $http reference explains the default mechanism how we should counter CSRF: The server has to set a cookie called XSRF-TOKEN. This cookie has to be Javascript readable, so that we can set the X-XSRF-TOKEN HTTP header in our requests. Is this mechanism sufficient to protect the refreh token scenario?

  1. Start the application the first time. No access token nor cookie available, we have to login with username and password. api/login gives us an access token that we keep in memory and sets two cookies. The HttpOnly refreh token cookie, and the JS readable XSRF-TOKEN cookie.

  2. The access token expires. A call to api/token validates XSRF-TOKEN and uses the token cookie to return a new access token; sets a new refresh cookie

  3. Restart the application from AppCache. No access token in memory but cookies available. Use api/token...

  4. Bad guy wants to steal our refreh cookie. A prepared page makes request to api/token with our cookies, but no X-XSRF-TOKEN HTTP header.

Any serious security issues?

回答1:

As far as I know the best way to do this is when server renders index.html with the CSFR token inside and after that you function as standard AngularJS SPA. So the index.html is then enriched with CSFR token generated by backend service / framework. SpringSecurity provides nice support for this injecting tokens to the templates.

After that, you can get the token from the template with javascript and set it to all your $http requests in headers by using httpInterceptor's, request hook. (or cookie)? I don't remember clearly whats the proper way but I am sure it is described in articles you mentioned above)