6

I am currently building a single page application with a JavaScript/HTML front end. The front end makes calls to a WEB API that was written in .NET. Currently I have an HTML page where a user enters their credentials into and clicks login. An AJAX request is then sent to the WEB API which authenticates the user and returns an HTTP-Only cookie that contains a Json Web Token. The browser then sends this cookie on each subsequent request, and the controller validates it.

The above is working well, however it lacks CSRF protection. I am trying to figure out the best way of implementing this. From my research, it looks like there are a few options.

  1. The article at https://www.jamesward.com/2013/05/13/securing-single-page-apps-and-rest-services suggests extracting a token from a cookie and then submitting that as a request header. In order to do this, I would need to drop the HTTP-Only flag, so the JavaScript can access the cookie. I don't think this is the best solution, as it could expose the session information if an XSS vulnerability is found. Is there something I am missing?

  2. Somewhere else I read about sending a separate cookie that contained the CSRF token and pulling the value out of that with JavaScript. Then sending that value as a custom header on subsequent AJAX requests and having the WEB API validate based on that header. I wrote a quick proof of concept application for this and discovered that the browser will still send the cookie the CSRF token was delivered in back on future requests. Does this pose an issue? Could a page with a CSRF attack somehow exploit this?

  3. Similar to the above, but instead of delivering the CSRF token via a cookie, deliver it via a custom header, and have JavaScript read it from there. This seems like it is the most straightforward, but do you see any drawbacks with this approach?

Also, is it safe to keep using a single CSRF token for a user's entire session, or should it be refreshed on every request?

  • "is it safe to keep using a single CSRF token for a user's entire session" - Best practice is for the CSRF token to be a nonce (number used only once). – demize Jan 4 '17 at 18:57
  • 2
    @demize citation required. Session CSRF tokens are common and fine, and allow for caching and back button expected behaviour – Neil McGuigan Jan 4 '17 at 20:33
  • the cookie is probably no good because it's automatically included in the request by the browser, even in a zombie XSS request. kill XSS, require POST, and you kill CSRF. you don't need a cookie anyway, you can print it into the page so that JS can see it. It doesn't really have to be a well-kept secret, just unique so that it can't be robotically forged ahead of time. – dandavis Jan 4 '17 at 22:07
1

You can use the classic CSRF token strategies, but they can take some awkward effort to use in AJAX-based applications, and easier options are available for AJAX-specific endpoints:

  • Add an extra header to requests to your server like "X-Requested-With: XMLHttpRequest" or "X-Is-Local-XHR: true", and make your server require the header on authenticated requests. Users with old versions of Flash may be vulnerable (though old versions of Flash have had much worse vulnerabilities too, so your choice on whether this is important to you). See https://stackoverflow.com/questions/17478731/whats-the-point-of-the-x-requested-with-header

  • Verify either the Origin or Referer header is set and matches the domain in the Host header. The Origin header is sent for all POST requests by Chrome and Firefox but older browsers may not include it. The Referer header is disabled by some users for privacy reasons. The presence of either with a valid value is enough to verify the request.

1

This is an old topic and there are many scattered discussions (including here on Security.SE). However, instead of the common suggestion of emulating a cookie-and-post-back-based CSRF protection scheme, I propose you do away with cookies altogether.

Treat you single-page app like a real APP and make it authenticate to Web APIs using the standard methods. For example, you can use OAuth tokens and session-less requests. (If OAuth is too difficult to setup, could use a custom token scheme.)

This also solves problems with CORS.

  • what problems with CORS are you referring to? – dandavis Jan 4 '17 at 22:11
  • @dandavis The fact that any resource that needs CORS probably can't use session cookies and will need alternative authentication like a token anyways. – billc.cn Jan 5 '17 at 12:19
  • Doesn't address multi-service workflows, oidc is probably also incompatible with this approach. – cal97g Sep 19 at 10:44

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.