Tag Archives: express

CSRF with NodeJS and Express 3

In a recent post I took a look at what it took to configure and use the CSRF protection middleware with Express. The article covered Express v2, and in the intervening few months, we’re already at the doorstep of Express v3, and the new version broke my example.

Express v3 did away with the dynamic helpers, so the code making use of them fails. Fortunately, the CSRF module still works fine; you just need to adjust how you’re using it.

Add the CSRF module to the app configuration in the same way as before. For example, on line 11 below, I add the CSRF middleware to the application. One important caveat is the CSRF module depends on the sessions middleware, so you need to configure session support as seen on line 10, and the CSRF middleware has to come after it in the configuration sequence.

app.configure(function(){
  app.set('port', process.env.PORT || 3000);
  app.set('views', __dirname + '/views');
  app.set('view engine', 'jade');
  app.use(express.favicon());
  app.use(express.logger('dev'));
  app.use(express.bodyParser());
  app.use(express.methodOverride());
  app.use(express.cookieParser('your secret here'));
  app.use(express.session());
  app.use(express.csrf());

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

  app.use(app.router);
  app.use(express.static(path.join(__dirname, 'public')));
});

Express v3 eliminated dynamic helpers, preferring instead that developers just use middleware. To facilitate setting variables available during template rendering, we can make use of the res.locals property. On line 13 above, we create a middleware handler which puts the CSRF token generated by the CSRF middleware into a variable called token which will be visible to the templates.

Note: this custom middleware to set a value in res.locals has to come before the app.router middleware defined on line 18, and after the app.csrf() middleware on line 12.

Once this is complete, there will be a variable called token available in all templates. This token should then be placed either into a hidden form field or query string parameter called _csrf on any HTTP request other than a GET,HEAD or OPTIONS request. If any other request type is made and this _csrf value is missing, the middleware will reject the request with an 403 - Forbidden status.

Here is an example of using the value in a Jade template for a form. Note on line 2 I create a hidden field for _csrf and set the value to the token property we created in the custom middleware above :

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

That’s all that is needed to adjust my previous example to work with Express v3.

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.