如何在node.js/express中测试受csrf保护的端点
我在express中实施了csrf(跨站点请求伪造)保护,如下所示:如何在node.js/express中测试受csrf保护的端点,node.js,angularjs,express,request,csrf,Node.js,Angularjs,Express,Request,Csrf,我在express中实施了csrf(跨站点请求伪造)保护,如下所示: ... app.use(express.csrf()); app.use(function (req, res, next) { res.cookie('XSRF-TOKEN', req.csrfToken()); next(); }); ... 这很有效。Angularjs在通过$http服务发出的所有请求中都使用csrf令牌。我通过angular应用程序提出的请求非常有效 我的问题是测试这些api端点。我使用mo
...
app.use(express.csrf());
app.use(function (req, res, next) {
res.cookie('XSRF-TOKEN', req.csrfToken());
next();
});
...
这很有效。Angularjs在通过$http服务发出的所有请求中都使用csrf令牌。我通过angular应用程序提出的请求非常有效
我的问题是测试这些api端点。我使用mocha来运行自动测试,并使用请求模块来测试api端点。当我使用请求模块向使用csrf(POST、PUT、DELETE等)的端点发出请求时,它会失败,即使它正确地使用了cookie等
还有谁能想出解决这个问题的办法吗?有人需要更多信息吗
测试示例:
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();
});
}
我要做的是仅在非生产环境中公开csrf令牌:
if (process.env.NODE_ENV !== 'production') {
app.use('/csrf', function (req, res, next) {
res.json({
csrf: req.csrfToken()
})
})
}
然后将其作为第一个测试,并将其保存为全局。您必须在测试中使用代理,以便始终使用相同的会话。诀窍在于,您需要将测试后的内容包装在GET中,并从cookie中解析必要的CSRF令牌。首先,假设您创建一个角度兼容的CSRF cookie,如下所示:
.use(express.csrf())
.use(function (req, res, next) {
res.cookie('XSRF-TOKEN', req.session._csrf);
res.locals.csrftoken = req.session._csrf;
next();
})
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();
});
});
然后,您的测试可以如下所示:
.use(express.csrf())
.use(function (req, res, next) {
res.cookie('XSRF-TOKEN', req.session._csrf);
res.locals.csrftoken = req.session._csrf;
next();
})
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();
});
});
他出色的回答非常有帮助。从那时起,supertest和模块的情况都发生了一些变化。因此,除了这个答案外,我还发现需要将以下内容传递给该职位:
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);
});
});
你能把你尝试做的测试的样本放在上面吗?然后我将如何在我的请求中使用csrf令牌?如果我将jar:true添加到请求对象,它使用cookies,我可以看到正在使用的令牌和cookie,但出于某种原因,它不接受它。那么我如何在请求中使用令牌使其接受它呢?您必须提交它。ie
x-csrf-token:csrf
或将其添加到请求正文中。是的,这很烦人,但这正是用户必须做的。确保会话不会在每次请求时都更改。我已经在测试端和服务器端注销了请求和响应对象,并且令牌在顺序请求中是相同的。它就在那里,就像我使用站点前端访问相同端点时一样。cookie就在那里,并且在第一个请求之后的每个请求中都会添加到请求体中。这种“麻烦”是好的,因为这是一种测试,可以确保表单确实需要csrf进行验证,并且不会忽略它。我不喜欢这种方法,因为你必须事先提出要求。但是我想不出一个不启动会话就获取(或存根)令牌的好方法。看起来就是这个方法。我希望我不必嵌套这样的请求,所以我可以在运行测试时禁用csrf。这当然有点痛苦,但如果你不使用csrf,你就无法实际进行端到端测试。这是真的。。。我还考虑过使用一个不同的测试框架,比如phantomjs,在这里我可以测试更多和应用程序的交互,而不仅仅是端点,但我想两者都可以。谢谢你的帮助!它与node.js配合得非常好!感谢分享您对cookies使用的想法!如果XSRF-TOKEN cookie不可用,并且您不希望将其添加到服务器中,则可以使用res.text.match(/name=_csrfvalue=([^>]*)>)>/)[1]
从登录页的html表单获取csrf令牌。