ember-ajax icon indicating copy to clipboard operation
ember-ajax copied to clipboard

Consider adding CSRF protection with Transparent Token security pattern

Open taras opened this issue 9 years ago • 23 comments

It seems appropriate that Ember Ajax should nudge developers towards good security practices by making it easy to implement good security practices. Transparent Token is protection against Cross-Site Request Forgery (CSRF).

Transparent Tokens mechanism works in the following way:

  1. Server provides CSRF token in the cookie
  2. Ember Ajax will check for the presence of this key in the cookie
  3. Ember Ajax will add the key's value into the headers of the request
  4. The server will verify the header against provided cookie value

Seems like a relatively straightforward implementation. Any reasons why we should not do this?

Here is a screenshot from presentation from presentation by Philippe de Ryck. screen shot 2016-09-10 at 11 15 25 am You can watch description of Transparent Token pattern here

taras avatar Sep 10 '16 09:09 taras

Definitely a good idea to do something like that. AngularJS does a similar thing, by automatically copying the XSRF-TOKEN from a cookie to a header, if present. More info here: https://docs.angularjs.org/api/ng/service/$http

philippederyck avatar Sep 10 '16 18:09 philippederyck

Is the name of that cookie something that's standard? Or would we need to provide a way for people to change that?

alexlafroscia avatar Sep 10 '16 20:09 alexlafroscia

As far as I know, there's not really a standard name for the cookie. Server-side frameworks/addons/libraries generally offer a way to configure the name, so I don't think there's much of a downside to pick one fixed name for the protection cookie.

One example library is csurf for Express (https://github.com/expressjs/csurf), which allows you to configure the name of the cookie. This is the only thing you'd need to configure, as they already look for the X-XSRF-TOKEN header in incoming requests by default.

To make the comparison to Angular again, I don't think you can configure the automatic behavior. You can always roll your own code if you need something special, something along the lines as shown in the picture above.

philippederyck avatar Sep 11 '16 06:09 philippederyck

I personnaly use django as backend, the cookie token is named csrftoken and the header token is named X-CSRFToken: https://docs.djangoproject.com/en/dev/ref/csrf/#ajax

The mechanics is similar but all names are differents.

GreatWizard avatar Oct 19 '16 10:10 GreatWizard

Maybe the best approach here would be to add some test cases or example code that show how to set this up manually, if there isn't a consistent way that we could implement it from our end.

alexlafroscia avatar Oct 19 '16 16:10 alexlafroscia

It seems that it only makes sense to settle on a default options (or a set of defaults) if there are frameworks that support this out of the box. That way, the dev automatically gets CSRF protection.

Regardless whether we settle on a default configuration, it seems that the most important aspect of this is actually informing developers that they should enable CSRF protection on both backend and frontend.

How would we do the latter? Add it to the official docs?

philippederyck avatar Oct 20 '16 06:10 philippederyck

We had at one point talked about making a website for ember-ajax but I don't think that's really necessary. Maybe we can add it to a Wiki page? I would really like to avoid adding even more to the bloat of the README and start making (and linking to) Wiki pages.

alexlafroscia avatar Oct 20 '16 18:10 alexlafroscia

Sounds fine to me. Let me know if I can help by providing some text to fit into the outline.

Since ember-ajax is only used in specific scenarios, I presume it would make sense to extend this solution towards other means of API access (e.g. ember-data)? Should this issue be documented on a higher level then?

philippederyck avatar Oct 20 '16 18:10 philippederyck

Possibly. There is also talk of removing the networking code from Ember Data and having it actually leverage Ember AJAX under the hood but I have no idea what the timeline on that will be.

If someone wants to write up some documentation on this, that would be great.

alexlafroscia avatar Oct 20 '16 21:10 alexlafroscia

Like I said, I don't mind writing something up on the topic. I think it's best if you create a page with an outline of how you'd like to inform people. I can fill in the blanks with a bit of info about the problem, and the solution.

We'll write it up for ember-ajax first, and then see how far up the chain we can take it :)

philippederyck avatar Oct 21 '16 06:10 philippederyck

I think @taras should take care of that, seeing as he started this issue.

alexlafroscia avatar Oct 21 '16 21:10 alexlafroscia

Would there be an easy way to disable this if it was added? The majority of use cases that I have, we're passing a token that the server requires so a CSRF is unneeded. Also, we utilize a lot of CORS, in which case a CSRF can't be used.

webark avatar Oct 25 '16 16:10 webark

@webark I think we've landed on the fact that this wouldn't be a code change, but rather documentation about how to do this if you want to. Unless we can implement a pattern that works for everyone, I would be hesitant to ship with support for something specific, and from the conversation earlier is seems like it'll depend on the backend. Unless I'm totally mistaken (and please, someone tell me if I am 😉 )

alexlafroscia avatar Oct 26 '16 01:10 alexlafroscia

ahhh.. I get it now. That makes sense. Thanks!

webark avatar Oct 26 '16 03:10 webark

Is there any work to be done here now? Or can we close this issue?

alexlafroscia avatar Jan 16 '17 04:01 alexlafroscia

For many API-only services CSRF isn't an issue (since there is no token tied to the domain). For example, of the session token is stored in local storage and passed along in a header.

For another category it's sufficient to add a custom header to all XHRs (e.g. X-My-Custom-Header: 1 and verify that it's set on the server (and reject requests otherwise). This is because the CSRF-vector use forms or flash to submit requests, and they cannot set custom headers.

There are some cases where CSRF must be considered (for example in mixed environments, where both JS and forms may create legitimate requests), but they are getting fewer as many are moving towards single-page apps and API-only backends.

Just wanted to note this in case others are stumbling upon this issue and are curious about CSRF.


I'd vote for just closing this issue. Possibly adding a note along the lines above to the readme under a CSRF heading.

sandstrom avatar Apr 24 '17 16:04 sandstrom

Thanks for picking this back up. You actually touch upon a few different things here, and I feel that I can clarify things even a bit further.

  1. You're right about the fact that CSRF does not matter if you're adding session information in a custom header. However, the fact that you use an API-only backend does not mean cookies are not a valid mechanism to keep session state.

The essence here is that if you use cookies, then you need to make sure you have CSRF covered

  1. Adding a custom header to all XHRs to prevent CSRF may work to filter out non-XHR requests, but this quickly brings you in the realm of CORS. This means that you'll need to make sure that incoming requests are from an origin you trust (you can use the Origin header here).

The essence here is that if you only offer an API (and use cookies), you should check the Origin header against a list of trusted origins. In addition, you can add the custom header as described above to ensure that only XHR can make such requests.

philippederyck avatar Apr 24 '17 17:04 philippederyck

@philippederyck I agree! Thanks for explaining this in a clear manner (you made a better job explaining it than I did). 😄

Perhaps we could just add a short section to the README.md under a CSRF heading and then close this issue. Would you mind PR:ing a text?

sandstrom avatar Apr 24 '17 17:04 sandstrom

You're welcome. I will put this on my TODO list for the coming weeks. I'm currently swamped with a couple of training courses, but I'll write something up in the first half of May.

Remind me if you think I may have forgotten about this!

philippederyck avatar Apr 24 '17 18:04 philippederyck

@philippederyck As the issue's still open and there's nothing obvious about it in the README, here's the reminder. :)

timhaines avatar Aug 05 '17 05:08 timhaines

Hello @philippederyck where can I find the CSRF section for Ember Ajax or if someone already has some documentation or example I would really appreciate it.

I would also like to know how CSRF can be implemented for Ember Data.

nato90 avatar Jan 03 '23 06:01 nato90

Wow, talk about something from the past. I unfortunately never got around to addressing this, and I haven't worked with Ember for a couple of years ...

These days, for SPAs backed by APIs, I typically recommend a tight CORS policy as a defense. This works well if ...

  • Do not perform state-changing operations with GETs
  • Require a JSON content type on all POSTs

If you really want to make sure there's no way to bypass CORS with CSRF, you could also require the client to include a custom header (e.g., X-Force-Preflight: 1)

philippederyck avatar Jan 03 '23 07:01 philippederyck

For others reading this:

If you really want to make sure there's no way to bypass CORS with CSRF, you could also require the client to include a custom header (e.g., X-Force-Preflight: 1)

This is an easy trick to shut down CSRF in an SPA! Note that this header name can be anything, as long as it's custom and that the backend require it.

sandstrom avatar Jan 03 '23 11:01 sandstrom