如何在启用 CSRF 安全性的情况下测试 Sails.js v1.0 登录控制器(使用 mocha、supertest)?
How can I test the Sails.js v1.0 login controller with CSRF security enabled (using mocha, supertest)?
我有一个几乎全新的 Sails.js 1.0.2 应用程序,我可以使用浏览器和 Postman 登录。但是,我似乎无法在我的测试运行程序中执行相同的过程。
下面的测试应该会导致成功登录,其中会返回一个带有新会话 ID 的 cookie。如果我将安全配置更改为禁用 CSRF protection,它会完美运行。但是启用安全性后,该请求将被禁止 (403)。我在 Postman 中发送的内容之间的唯一实质性区别似乎是 mocha 在不同的端口上运行应用程序(Postman 发送到 localhost:1337
,express' res 变量说 PUT /api/v1/entrance/login HTTP/1.1 Host: 127.0.0.1:56002
有人看到我遗漏了什么吗?
这是测试文件:
/**
* /test/integration/controllers/entrance/login.test.js
*/
'use strict';
const supertest = require('supertest'); // also tried supertest-session
describe('Entrance controllers', () => {
describe('/api/v1/entrance/login', () => {
before(() => {
return supertest(sails.hooks.http.app)
.get('/login')
.then(res => {
const reTokenCapture = /_csrf:\s*unescape\('([^']+)'\)/;
const found = reTokenCapture.exec(res.text);
this._csrf = sails.config.security.csrf ? found[1] : '';
this.url = '/api/v1/entrance/login';
});
});
it('should return a session cookie in response headers', () => {
return supertest(sails.hooks.http.app)
.put(this.url)
.set('x-csrf-token', this._csrf)
.send({
emailAddress: 'admin@example.com',
password: 'abc123',
// _csrf: this._csrf, // I tried this too; no luck
})
.expect(200) // if sails.config.security.csrf is enabled, status is 403
.then(res => {
// console.log('res:', res); // this shows the correct header
res.headers['set-cookie'].should.be.an('array');
const hasSid = res.headers['set-cookie'].map(cookie => {
const reSid = /^sails\.sid=[^;]+;\sPath=\/;(?:\sExpires=[^;]+GMT;)?\sHttpOnly$/;
return reSid.test(cookie);
});
hasSid.should.include.members([true]);
});
});
});
});
我是 运行 node v8.11.3,sails v1.0.2,mocha v5.2.0,supertest v3.1.0,chai v4.1.2
仅供参考,这是 Postman 提出的工作正常的请求(CSRF 令牌由先前的 Postman 请求手动复制到 GET /login
):
PUT /api/v1/entrance/login HTTP/1.1
Host: localhost:1337
x-csrf-token: mjWXQTa2-RFEHu78Tr-JGJwhWeryKGRJI4S8
Cache-Control: no-cache
Postman-Token: e3d920fe-6178-4642-80e4-8005b477fd98
{"emailAddress": "admin@example.com", "password":"abc123"}
知道了!我以为我打算在登录后从 set-cookie
header 获取 session ID。相反,我打算同时捕获 CSRF 令牌和 session ID仍然注销,然后提交电子邮件和密码,然后在后续请求中使用令牌和 ID。我在 Postman 中错过了这个细节,因为我没有注意到在请求之间持续存在的 cookie。
这是固定的测试文件(现在可以在启用 CSRF 保护的情况下使用):
/**
* /test/integration/controllers/entrance/login.test.js
*/
'use strict';
const supertest = require('supertest'); // also tried
describe('Entrance controllers', () => {
describe('/api/v1/entrance/login', () => {
before(() => {
this._url = '/api/v1/entrance/login';
return supertest(sails.hooks.http.app).get('/login')
.then(getRes => {
const reTokenCapture = /_csrf:\s*unescape\('([^']+)'\)/;
const foundToken = reTokenCapture.exec(getRes.text);
this._csrf = sails.config.security.csrf ? foundToken[1] : '';
this._cookie = getRes.headers['set-cookie'].join('; ');
});
});
it('should accept the session ID & CSRF token procured by GET /login', () => {
return supertest(sails.hooks.http.app)
.put(this._url)
.set('Cookie', this._cookie)
.set('X-CSRF-Token', this._csrf)
.send({
emailAddress: 'admin@example.com',
password: 'abc123',
})
.expect(200);
});
it('should reject requests without a CSRF token', () => {
return supertest(sails.hooks.http.app)
.put(this._url)
.set('Cookie', this._cookie)
.expect(403);
});
it('should reject requests without a session cookie', () => {
return supertest(sails.hooks.http.app)
.put(this._url)
.set('Cookie', '')
.set('x-csrf-token', this._csrf)
.expect(403);
});
it('should reject requests with invalid tokens', () => {
return supertest(sails.hooks.http.app)
.put(this._url)
.set('Cookie', 'sails.sid=foo; Path=/; HttpOnly')
.set('X-CSRF-Token', 'foo')
.send({
emailAddress: 'admin@example.com',
password: 'abc123',
})
.expect(403);
});
it('should reject requests with invalid credentionals', () => {
return supertest(sails.hooks.http.app)
.put(this._url)
.set('Cookie', this._cookie)
.set('X-CSRF-Token', this._csrf)
.send({
emailAddress: 'user@example.com',
password: 'password'
})
.expect(401);
});
it('should reject get requests', () => {
return supertest(sails.hooks.http.app)
.get(this._url)
.set('Cookie', this._cookie)
.set('X-CSRF-Token', this._csrf)
.send({
emailAddress: 'admin@example.com',
password: 'abc123',
})
.expect(404);
});
it('should reject post requests', () => {
return supertest(sails.hooks.http.app)
.post(this._url)
.set('Cookie', this._cookie)
.set('X-CSRF-Token', this._csrf)
.send({
emailAddress: 'admin@example.com',
password: 'abc123',
})
.expect(404);
});
});
});
我有一个几乎全新的 Sails.js 1.0.2 应用程序,我可以使用浏览器和 Postman 登录。但是,我似乎无法在我的测试运行程序中执行相同的过程。
下面的测试应该会导致成功登录,其中会返回一个带有新会话 ID 的 cookie。如果我将安全配置更改为禁用 CSRF protection,它会完美运行。但是启用安全性后,该请求将被禁止 (403)。我在 Postman 中发送的内容之间的唯一实质性区别似乎是 mocha 在不同的端口上运行应用程序(Postman 发送到 localhost:1337
,express' res 变量说 PUT /api/v1/entrance/login HTTP/1.1 Host: 127.0.0.1:56002
有人看到我遗漏了什么吗?
这是测试文件:
/**
* /test/integration/controllers/entrance/login.test.js
*/
'use strict';
const supertest = require('supertest'); // also tried supertest-session
describe('Entrance controllers', () => {
describe('/api/v1/entrance/login', () => {
before(() => {
return supertest(sails.hooks.http.app)
.get('/login')
.then(res => {
const reTokenCapture = /_csrf:\s*unescape\('([^']+)'\)/;
const found = reTokenCapture.exec(res.text);
this._csrf = sails.config.security.csrf ? found[1] : '';
this.url = '/api/v1/entrance/login';
});
});
it('should return a session cookie in response headers', () => {
return supertest(sails.hooks.http.app)
.put(this.url)
.set('x-csrf-token', this._csrf)
.send({
emailAddress: 'admin@example.com',
password: 'abc123',
// _csrf: this._csrf, // I tried this too; no luck
})
.expect(200) // if sails.config.security.csrf is enabled, status is 403
.then(res => {
// console.log('res:', res); // this shows the correct header
res.headers['set-cookie'].should.be.an('array');
const hasSid = res.headers['set-cookie'].map(cookie => {
const reSid = /^sails\.sid=[^;]+;\sPath=\/;(?:\sExpires=[^;]+GMT;)?\sHttpOnly$/;
return reSid.test(cookie);
});
hasSid.should.include.members([true]);
});
});
});
});
我是 运行 node v8.11.3,sails v1.0.2,mocha v5.2.0,supertest v3.1.0,chai v4.1.2
仅供参考,这是 Postman 提出的工作正常的请求(CSRF 令牌由先前的 Postman 请求手动复制到 GET /login
):
PUT /api/v1/entrance/login HTTP/1.1
Host: localhost:1337
x-csrf-token: mjWXQTa2-RFEHu78Tr-JGJwhWeryKGRJI4S8
Cache-Control: no-cache
Postman-Token: e3d920fe-6178-4642-80e4-8005b477fd98
{"emailAddress": "admin@example.com", "password":"abc123"}
知道了!我以为我打算在登录后从 set-cookie
header 获取 session ID。相反,我打算同时捕获 CSRF 令牌和 session ID仍然注销,然后提交电子邮件和密码,然后在后续请求中使用令牌和 ID。我在 Postman 中错过了这个细节,因为我没有注意到在请求之间持续存在的 cookie。
这是固定的测试文件(现在可以在启用 CSRF 保护的情况下使用):
/**
* /test/integration/controllers/entrance/login.test.js
*/
'use strict';
const supertest = require('supertest'); // also tried
describe('Entrance controllers', () => {
describe('/api/v1/entrance/login', () => {
before(() => {
this._url = '/api/v1/entrance/login';
return supertest(sails.hooks.http.app).get('/login')
.then(getRes => {
const reTokenCapture = /_csrf:\s*unescape\('([^']+)'\)/;
const foundToken = reTokenCapture.exec(getRes.text);
this._csrf = sails.config.security.csrf ? foundToken[1] : '';
this._cookie = getRes.headers['set-cookie'].join('; ');
});
});
it('should accept the session ID & CSRF token procured by GET /login', () => {
return supertest(sails.hooks.http.app)
.put(this._url)
.set('Cookie', this._cookie)
.set('X-CSRF-Token', this._csrf)
.send({
emailAddress: 'admin@example.com',
password: 'abc123',
})
.expect(200);
});
it('should reject requests without a CSRF token', () => {
return supertest(sails.hooks.http.app)
.put(this._url)
.set('Cookie', this._cookie)
.expect(403);
});
it('should reject requests without a session cookie', () => {
return supertest(sails.hooks.http.app)
.put(this._url)
.set('Cookie', '')
.set('x-csrf-token', this._csrf)
.expect(403);
});
it('should reject requests with invalid tokens', () => {
return supertest(sails.hooks.http.app)
.put(this._url)
.set('Cookie', 'sails.sid=foo; Path=/; HttpOnly')
.set('X-CSRF-Token', 'foo')
.send({
emailAddress: 'admin@example.com',
password: 'abc123',
})
.expect(403);
});
it('should reject requests with invalid credentionals', () => {
return supertest(sails.hooks.http.app)
.put(this._url)
.set('Cookie', this._cookie)
.set('X-CSRF-Token', this._csrf)
.send({
emailAddress: 'user@example.com',
password: 'password'
})
.expect(401);
});
it('should reject get requests', () => {
return supertest(sails.hooks.http.app)
.get(this._url)
.set('Cookie', this._cookie)
.set('X-CSRF-Token', this._csrf)
.send({
emailAddress: 'admin@example.com',
password: 'abc123',
})
.expect(404);
});
it('should reject post requests', () => {
return supertest(sails.hooks.http.app)
.post(this._url)
.set('Cookie', this._cookie)
.set('X-CSRF-Token', this._csrf)
.send({
emailAddress: 'admin@example.com',
password: 'abc123',
})
.expect(404);
});
});
});