CSRF Protection in ExpressJS CSRF Protection in ExpressJS express express

CSRF Protection in ExpressJS


One option is to add a hidden input field to all your forms as you mention. But according to the Express docs on csrf:

The default value function checks req.body generated by the bodyParser() middleware, req.query generated by query(), and the "X-CSRF-Token" header field.

So depending on your client side framework, you could also use the query string or the X-CSRF-Token alternatives.

The point remains that you need to:

  • pass the _.csrf token from Express to your client side
  • return the _.csrf token from the client side back to Express on all your state mutating reqs (POST/PUT/DELETE) so Express can compare it against the req.session._csrf to complete the cycle.

For example if your client side is in Angular, the $http module offers csrf protection by default, looking for a cookie called XSRF-TOKEN and returning this value on all state mutating requests (POST/PUT/DELETE) through a header calledX-XSRF-TOKEN. This is an unlucky coincidence, because the name differs from the header name where Express looks for it, which is X-CSRF-TOKEN (notice -XSRF- vs. -CSRF-).

To overcome this you need to

Step 1: On the Express side augment the default value function of the CSRF middleware to look for the token value in the X-XSRF-TOKEN header, in addition to all other default places:

app.use(express.csrf({value: function(req) {    var token = (req.body && req.body._csrf) ||         (req.query && req.query._csrf) ||         (req.headers['x-csrf-token']) ||         // This is the only addition compared to the default value function        (req.headers['x-xsrf-token']);    return token;    }});

Step 2: On the Express side again set the token value added by the CSRF middleware under req.session._csrf in the cookie that Angular will look for, using a custom middleware:

app.use(function(req, res, next) {    req.cookie('XSRF-TOKEN', req.session._csrf);    next();});

Now Angular will find it and include it in the X-XSRF-TOKEN header without any further action.


Further down in that article, the author explains that this exposes a "token" property to all of your templates that should be included on a hidden input field.

Notice the 2nd line in his jade example:

form(action='/form',method='post')  input(type='hidden', name='_csrf', value=token)  label(for='color') Color:  input(type='text',name='color',size='50')  button(type='submit') Save