CSRF with NodeJS and Express

(Note: see my updated post for Express v3 compatibility)

I’ve been trying to dig into how to handle the common OWASP web security threats with NodeJS + Express and was hitting my head against the wall on getting the Connect CSRF middleware working correctly with Express. It turned out to be pretty simple once I found all the pieces. For the sake of others, here’s how I got it working.

First, in app.js, configure the middleware. Here’s the block I use:

app.configure(function(){
  app.set('views', __dirname + '/views');
  app.set('view engine', 'jade');
  app.use(express.logger('short'));
  app.use(express.cookieParser());
  app.use(express.session({ secret: "changeme" }));
  app.use(express.bodyParser());
  app.use(express.methodOverride());
  app.use(express.csrf());
  app.use(express.favicon());
  app.use(app.router);
  app.use(express.static(__dirname + '/public'));
});

Line 9 adds the CSRF middleware, but you also need lines 5 & 6, as the CSRF middleware depends on session support to save the CSRF token on the server.

Once this is configured, you can also add a dynamic handler to simplify inserting the token into your forms. This isn’t strictly required, but it reduces a lot of redundant code if you have multiple forms. The dynamic helper configuration looks like this:

app.dynamicHelpers({
    token: function(req, res) {
        return req.session._csrf;
    }
});

I’m working with a default Express application, so I’ll add two routes for a form to app.js. The mapping looks like this:

app.get('/form', routes.show);
app.post('/form', routes.save);

And in routes/index.js I added these two handlers:

exports.show = function(req, res) {
    var msg = req.flash('info');
    res.render('form', {title: 'Form Demo', message: msg});
};

exports.save = function(req,res) {
    req.flash('info','You picked %s', req.body.color);
    res.redirect('/form');
};

Finally, to get it all working, create the file views/form.jade with the following content:

p #{message}
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

Line 3 is where the magic happens. You need to add a hidden form element with a name of _csrf and the value is derived from calling the dynamic helper we configured above.

Start the application and browse to http://localhost:3000/form and you should see our basic form. If you take a look at the code for the page, you’ll see the form looks like this:

The CSRF token has been inserted in to the form. To test things out, fire up another terminal window and use a cURL request to test it. Run curl -X POST http://localhost:3000/form. The response will be Forbidden. This is due to the CSRF middleware intercepting the POST request and attempting to find the _csrf parameter and compare it to the value the CSRF middleware generated. If the parameter isn’t found, or is invalid, the CSRF middleware will return the HTTP 403 Forbidden status.

The CSRF middleware will look for the CSRF token in three locations. First, it will check for a _csrf property in the request body (req.body._csrf), next it will check fot it on the query string (req.query._csrf), and finally, it will check the request headers (req.headers['x-csrf-token']).

So this takes care of OWASP Risk #5. I’ll take a look at how NodeJS and Express deals with the other OWASP in future posts.

4 thoughts on “CSRF with NodeJS and Express”

  1. Thanks a lot for your article, it has helped me after continuous efforts of getting CSRF running with express.

    In this article http://dailyjs.com/2012/09/13/express-3-csrf-tutorial/ express.csrf was configured after router configuration, which never worked for me as validation of csrf token was not happening.

    The correct usage was spotted from your article.

    app.use(express.csrf());
    app.use(app.router);

    Thanks,
    -Vikas.

Leave a Reply

Your email address will not be published. Required fields are marked *

*