简体   繁体   English

如何在node.js / express中测试受csrf保护的端点

[英]How to test endpoints protected by csrf in node.js/express

I have implemented csrf (cross-site request forgery) protection in an express like so: 我在快递中实现了csrf(跨站点请求伪造)保护,如下所示:

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

This works great. 这非常有效。 Angularjs utilized the csrf token in all requests made through the $http service. Angularjs在通过$ http服务发出的所有请求中使用了csrf令牌。 The requests that I make through my angular app work great. 我通过我的角度应用程序发出的请求非常好。

My problem is testing these api endpoints. 我的问题是测试这些api端点。 I'm using mocha to run my automated tests and the request module to test my api endpoints. 我正在使用mocha运行我的自动化测试和请求模块来测试我的api端点。 When I make a request to an endpoint that utilizes csrf (POST, PUT, DELETE, etc.) using the request module, it fails, even though it correctly utilizes cookies and such. 当我使用请求模块向使用csrf(POST,PUT,DELETE等)的端点发出请求时,即使它正确使用了cookie等,它也会失败。

Has anybody else come up with a solution to this? 还有其他人提出解决方案吗? Does anyone need more information? 有人需要更多信息吗?

Example of test: 测试示例:

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();
  });
}

The trick is that you need to wrap your POST test inside a GET and parse the necessary CSRF token from the cookie. 诀窍是你需要将你的POST测试包装在GET中并从cookie中解析必要的CSRF令牌。 First, this assumes you create an Angular-compatible CSRF cookie like this: 首先,假设您创建一个与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();
})

Then, your test could look like this: 然后,您的测试可能如下所示:

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();
  });
});

The idea is to actually login and use post the CSRF token you're getting from the cookie. 我们的想法是实际登录并使用发布您从cookie中获取的CSRF令牌。 Note that you need the following at the top of the mocha test file: 请注意,您需要在mocha测试文件的顶部添加以下内容:

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

what i do is expose a csrf token only in non-production: 我所做的只是在非生产中暴露一个csrf令牌:

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

then have it be the first test and save it as a global. 然后让它成为第一个测试并将其保存为全局测试。 you'll have to use an agent in your tests so you consistently use the same session. 您必须在测试中使用代理,以便始终使用相同的会话。

@dankohn 's excellent answer has been most helpful. @dankohn的出色答案最有帮助。 Things have changed a bit since then, regarding both supertest and the csurf module. 关于supertest和csurf模块,事情已经发生了一些变化。 Therefore, in addition to that answer, I have found that the following needs to be passed to the POST: 因此,除了答案之外,我发现需要将以下内容传递给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);
      });
  });

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM