如何在node.js / express中testing由csrf保护的端点

我已经实施了csrf(跨站请求伪造)保护,如下所示:

... app.use(express.csrf()); app.use(function (req, res, next) { res.cookie('XSRF-TOKEN', req.csrfToken()); next(); }); ... 

这很好。 Angularjs在通过$ http服务所做的所有请求中都使用了csrf标记。 我通过我的angular度应用程序的要求很好。

我的问题是testing这些API端点。 我使用摩卡运行我的自动化testing和请求模块来testing我的API端点。 当我使用请求模块向使用csrf(POST,PUT,DELETE等)的端点发出请求时,即使正确使用了cookie等,它也会失败。

有其他人想出了解决办法吗? 有没有人需要更多的信息?

testing示例:

 function testLogin(done) { request({ method: 'POST', url: baseUrl + '/api/login', json: { email: 'myemail@email.com', password: 'mypassword' } }, function (err, res, body) { // do stuff to validate returned data // the server spits back a 'FORBIDDEN' string, // which obviously will not pass my validation // criteria done(); }); } 

诀窍是你需要将你的POSTtesting封装在一个GET中,并从cookie中parsing出必要的CSRF标记。 首先,假定您创build一个类似于Angular的CSRF cookie:

 .use(express.csrf()) .use(function (req, res, next) { res.cookie('XSRF-TOKEN', req.session._csrf); res.locals.csrftoken = req.session._csrf; next(); }) 

那么,你的testing可能是这样的:

 describe('Authenticated Jade tests', function () { this.timeout(5000); before(function (done) { [Set up an authenticated user here] }); var validPaths = ['/help', '/products']; async.each(validPaths, function (path, callback) { it('should confirm that ' + path + ' serves HTML and is only available when logged in', function (done) { request.get('https://127.0.0.1:' + process.env.PORT + path, function (err, res, body) { expect(res.statusCode).to.be(302); expect(res.headers.location).to.be('/login'); expect(body).to.be('Moved Temporarily. Redirecting to /login'); var csrftoken = unescape(/XSRF-TOKEN=(.*?);/.exec(res.headers['set-cookie'])[1]); var authAttributes = { _csrf: csrftoken, email: userAttributes.email, password: 'password' }; request.post('https://127.0.0.1:' + process.env.PORT + '/login', { body: authAttributes, json: true }, function (err, res) { expect(res.statusCode).to.be(303); request.get('https://127.0.0.1:' + process.env.PORT + path, function (err, res, body) { expect(res.statusCode).to.be(200); expect(body.toString().substr(-14)).to.be('</body></html>'); request.get('https://127.0.0.1:' + process.env.PORT + '/bye', function () { done(); }); }); }); }); }); callback(); }); }); 

这个想法是实际login并使用您从cookie获得的CSRF令牌。 请注意,你需要在摩卡testing文件的顶部以下:

 var request = require('request').defaults({jar: true, followRedirect: false}); 

我所做的只是在非生产中暴露一个csrf标记:

 if (process.env.NODE_ENV !== 'production') { app.use('/csrf', function (req, res, next) { res.json({ csrf: req.csrfToken() }) }) } 

那么将它作为第一个testing并将其保存为全球。 您必须在testing中使用代理,以便始终使用同一个会话。

@dankohn的优秀答案是最有帮助的。 从那以后,事情已经发生了一些变化,关于supertest和csurf模块。 因此,除了这个答案之外,我发现需要将以下内容传递给POST:

  it('should ...', function(done) { request(app) .get('/...') .expect(200) .end(function(err, res) { var csrfToken = unescape(/XSRF-TOKEN=(.*?);/.exec(res.headers['set-cookie'])[1]); assert(csrfToken); request(app) .post('/...') .set({cookie: res.headers['set-cookie']}) .send({ _csrf: csrfToken, ... }) .expect(200) .end(done); }); });