How to implement CSRF protection in Ajax calls using express.js (looking for complete example)?

Benjen picture Benjen · Jun 26, 2012 · Viewed 13k times · Source

I am trying to implement CSRF protection in an app built using node.js using the express.js framework. The app makes abundant use of Ajax post calls to the server. I understand that the connect framework provides CSRF middleware, but I am not sure how to implement it in the scope of client-side Ajax post requests.

There are bits and pieces about this in other Questions posted here in stackoverflow, but I have yet to find a reasonably complete example of how to implement it from both the client and server sides.

Does anyone have a working example they care to share on how to implement this? Most of the examples I have seen, assume you are rendering the form on the server-side and then sending it (along with the embedded csrf_token form field) to the client-side. In my app, all content is rendered on the client-side (including templates) via Backbone.js. All the server does is provide values in JSON format, which are utilized by various Models in Backbone.js on the client-side. By my understanding I would need to retrieve the csrf_token via ajax first before it can be used. However, I am concerned this may be problematic from a security standpoint. Is this a valid concern?

Answer

Fizer Khan picture Fizer Khan · Aug 4, 2013

It can be done by adding meta tag for CSRF token and then pass CSRF token with every Ajax request

Server

Add CSRF middleware

app.use(express.csrf());
app.use(function (req, res, next) {
  res.locals.token = req.session._csrf;
  next();
});

You can pass a CSRF token to the client side via, say, a meta tag. For ex, in Jade

meta(name="csrf-token", content="#{token}")

Client

jQuery has a feature called ajaxPrefilter, which lets you provide a callback to be invoked every Ajax request. Then set a header using ajaxPrefilter.

var CSRF_HEADER = 'X-CSRF-Token';

var setCSRFToken = function (securityToken) {
  jQuery.ajaxPrefilter(function (options, _, xhr) {
    if (!xhr.crossDomain) {
      xhr.setRequestHeader(CSRF_HEADER, securityToken);
    }
  });
};

setCSRFToken($('meta[name="csrf-token"]').attr('content'));