我已经在快递中实施了csrf(跨站点请求伪造)保护:
...
app.use(express.csrf());
app.use(function (req, res, next) {
res.cookie('XSRF-TOKEN', req.csrfToken());
next();
});
...
这很有效。 Angularjs在通过$ http服务发出的所有请求中使用了csrf令牌。我通过我的角度应用程序发出的请求很有效。
我的问题是测试这些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();
});
}
答案 0 :(得分:10)
诀窍是你需要将你的POST测试包装在GET中并从cookie中解析必要的CSRF令牌。首先,假设您创建一个与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();
})
然后,您的测试可能如下所示:
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();
});
});
这个想法是实际登录并使用发布你从cookie获得的CSRF令牌。请注意,您需要在mocha测试文件的顶部添加以下内容:
var request = require('request').defaults({jar: true, followRedirect: false});
答案 1 :(得分:1)
我所做的只是在非生产中公开csrf令牌:
if (process.env.NODE_ENV !== 'production') {
app.use('/csrf', function (req, res, next) {
res.json({
csrf: req.csrfToken()
})
})
}
然后让它成为第一个测试并将其保存为全局测试。您必须在测试中使用代理,以便始终使用相同的会话。
答案 2 :(得分:1)
@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);
});
});