diff --git a/app.js b/app.js index 13e1bd6..5c4c16a 100644 --- a/app.js +++ b/app.js @@ -13,6 +13,11 @@ env.express(app); app.use(express.cookieParser()); app.use(middleware.session()); +app.use(middleware.csrf({ + whitelist: [ + '/applications' + ] +})); app.use(express.logger({stream:{ write: function(msg, encoding) { logger.info(msg.trim()); @@ -20,7 +25,6 @@ app.use(express.logger({stream:{ }})); app.use(express.compress()); app.use(express.bodyParser()); -app.use(express.csrf()); app.use(express.static(path.join(__dirname, 'static'))); app.use(flash()); diff --git a/middleware.js b/middleware.js index 057f862..3c8bf14 100644 --- a/middleware.js +++ b/middleware.js @@ -1,3 +1,4 @@ +var errors = require('./lib/errors'); var express = require('express'); var _ = require('underscore'); @@ -25,3 +26,57 @@ exports.session = function session (config) { }) }); }; + +exports.csrf = function (options) { + options = options || {}; + var value = options.value || defaultCsrfValue; + var list = options.whitelist; + return function (req, res, next) { + if (whitelisted(list, req.url)) + return next(); + + var token = req.session._csrf || (req.session._csrf = uid(24)); + + if ('GET' === req.method || 'HEAD' === req.method) + return next(); + + var val = value(req); + if (val != token) { + // logger.debug("CSRF token failure"); + return next(errors.Forbidden()); + } + next(); + }; +}; + +function defaultCsrfValue (req) { + return (req.body && req.body._csrf) + || (req.query && req.query._csrf) + || (req.headers['x-csrf-token']); +} + +function whitelisted (list, input) { + var pattern; + + for (var i = list.length; i--;) { + pattern = list[i]; + if (RegExp('^' + list[i] + '$').test(input)) + return true; + } + + return false; +} + +function uid (len) { + var buf = []; + var chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; + var charlen = chars.length; + for (var i = 0; i < len; ++i) { + buf.push(chars[getRandomInt(0, charlen - 1)]); + } + return buf.join(''); +}; + +function getRandomInt (min, max) { + return Math.floor(Math.random() * (max - min + 1)) + min; +}