Skip to content

Commit

Permalink
added csrf problem w/ tests.
Browse files Browse the repository at this point in the history
  • Loading branch information
toolness committed Sep 25, 2013
1 parent 5d6ec6d commit 9f9dd78
Show file tree
Hide file tree
Showing 4 changed files with 72 additions and 6 deletions.
3 changes: 2 additions & 1 deletion bin/verify.js
Expand Up @@ -12,7 +12,8 @@ var PROBLEMS = {
'redos': 'Regular Expression Denial of Service',
'reflected-xss': 'Reflected Cross-Site Scripting',
'httponly': 'HttpOnly Cookie',
'csp': 'Content Security Policy'
'csp': 'Content Security Policy',
'csrf': 'Cross-Site Request Forgery'
};

function help() {
Expand Down
15 changes: 12 additions & 3 deletions test/lib/app-request.js
Expand Up @@ -5,19 +5,28 @@ var app = util.getApp();

module.exports = function appRequest(options, cb) {
var jar = request.jar();
var session = {};

if (typeof(options) == 'string') options = {url: options};

if (!('followAllRedirects' in options))
options.followAllRedirects = true;

if (options.method == 'POST' && !options.ignoreCsrfToken) {
session.csrfToken = "TESTING";
if (!options.form) options.form = {};
options.form.csrfToken = session.csrfToken;
}
delete options.ignoreCsrfToken;

if ('user' in options) {
jar.add(request.cookie(app.sessionCookie.serialize({
user: options.user
})));
session.user = options.user;
delete options.user;
}

if (Object.keys(session).length)
jar.add(request.cookie(app.sessionCookie.serialize(session)));

options.jar = jar;
app.db = options.db || util.level();
delete options.db;
Expand Down
9 changes: 7 additions & 2 deletions test/problems/csrf.js
Expand Up @@ -5,11 +5,15 @@ var testUtil = require('../lib');
var sessionCookie = testUtil.getApp().sessionCookie;
var appRequest = testUtil.appRequest;

// TODO: Ensure that the solution uses crypto.getRandomBytes().

test("GET / sets CSRF token in session cookie, login form", function(t) {
appRequest('/', function(err, res, body) {
t.notOk(err);
var setCookieHeaders = res.headers['set-cookie'];
t.equal((setCookieHeaders || []).length, 1,
t.ok(setCookieHeaders, "set-cookie header must be present");
if (!setCookieHeaders) return t.end();
t.equal(setCookieHeaders.length, 1,
'one set-cookie header is provided');
var session = sessionCookie.parse(setCookieHeaders[0]);
t.ok(session, "session cookie exists");
Expand All @@ -28,7 +32,8 @@ function test403(path) {
test("POST " + path + " without csrfToken returns 403", function(t) {
appRequest({
method: 'POST',
url: '/'
url: '/',
ignoreCsrfToken: true
}, function(err, res, body) {
t.notOk(err);
t.equal(res.statusCode, 403);
Expand Down
51 changes: 51 additions & 0 deletions test/solutions/csrf.diff
@@ -0,0 +1,51 @@
--- app-vulnerable.js 2013-09-25 00:52:26.000000000 -0400
+++ app.js 2013-09-25 01:04:14.000000000 -0400
@@ -10,8 +10,12 @@
var views = {
401: function() { return 'You must <a href="/">log in</a> first.'; },
404: function() { return "Alas, this is a 404."; },
+ _csrf: function(req) {
+ return ' <input type="hidden" name="csrfToken" value="' +
+ req.session.csrfToken + '">';
+ },
login: function(req) { return [
- '<form method="post" action="/login">',
+ '<form method="post" action="/login">', this._csrf(req),
' username: <input type="text" name="username" required>',
' password: <input type="password" name="password" required>',
' <button type="submit" name="action" value="login">Login</button>',
@@ -20,10 +24,10 @@
'</form>'
].join('\n'); },
notes: function(req, notes) { return [
- '<form method="post" action="/logout">',
+ '<form method="post" action="/logout">', this._csrf(req),
' <input type="submit" value="Logout ' + req.session.user + '">',
'</form>',
- '<form method="post">',
+ '<form method="post">', this._csrf(req),
' <textarea cols="80" rows="20" name="notes">' + notes + '</textarea>',
' <input type="submit" value="Update Notes">',
'</form>'
@@ -59,6 +63,11 @@

var routes = {
'GET /': function showLoginFormOrUserNotes(req, res) {
+ if (!req.session.csrfToken) {
+ req.session.csrfToken = require('crypto').randomBytes(36).toString('base64');
+ res.setHeader("Set-Cookie", sessionCookie.serialize(req.session));
+ }
+
if (req.query.msg)
res.write('<hr><em>' + Buffer(req.query.msg, 'hex') + '</em><hr>\n');
if (!req.session.user)
@@ -145,6 +154,9 @@
req.on('end', function() {
var data = Buffer.concat(bodyChunks).toString();
req.body = querystring.parse(data);
+ if (!(req.body.csrfToken && req.session.csrfToken &&
+ req.body.csrfToken == req.session.csrfToken))
+ return next(403);
route(req, res, next);
});
} else route(req, res, next);

0 comments on commit 9f9dd78

Please sign in to comment.