diff --git a/src/agent-base.js b/src/agent-base.js index 45bd6d470..be189aa60 100644 --- a/src/agent-base.js +++ b/src/agent-base.js @@ -24,7 +24,8 @@ function Agent() { 'key', 'pfx', 'cert', - 'disableTLSCerts' + 'disableTLSCerts', + 'sendSecureCookie' ].forEach(fn => { // Default setting for all requests from this agent Agent.prototype[fn] = function(...args) { diff --git a/src/node/agent.js b/src/node/agent.js index 7aa5fd037..d5573458c 100644 --- a/src/node/agent.js +++ b/src/node/agent.js @@ -50,6 +50,10 @@ function Agent(options) { if (options.rejectUnauthorized === false) { this.disableTLSCerts(); } + + if (options.sendSecureCookie) { + this.sendSecureCookie(); + } } } @@ -76,11 +80,14 @@ Agent.prototype._saveCookies = function(res) { */ Agent.prototype._attachCookies = function(req) { + const sendSecureCookie = Boolean( + this._defaults.find(current => current.fn === 'sendSecureCookie') + ); const url = parse(req.url); const access = new CookieAccessInfo( url.hostname, url.pathname, - url.protocol === 'https:' + url.protocol === 'https:' || sendSecureCookie ); const cookies = this.jar.getCookies(access).toValueString(); req.cookies = cookies; diff --git a/src/node/index.js b/src/node/index.js index 7c3e0cd9b..614a0255c 100644 --- a/src/node/index.js +++ b/src/node/index.js @@ -646,6 +646,19 @@ Request.prototype.disableTLSCerts = function() { return this; }; +/** + * Sends secure cookies on http and https requests + * Be warned this allows cookie hijacking + * + * @return {Request} for chaining + * @api public + */ + +Request.prototype.sendSecureCookie = function() { + this._sendSecureCookie = true; + return this; +}; + /** * Return an http[s] request. * diff --git a/test/node/secure-cookie.js b/test/node/secure-cookie.js new file mode 100644 index 000000000..8bb782042 --- /dev/null +++ b/test/node/secure-cookie.js @@ -0,0 +1,82 @@ +const request = require('../support/client'); +const express = require('express'); +const cookieParser = require('cookie-parser'); +const http = require('http'); + +const app = express(); + +app.use(cookieParser()); + +app.get('/', (req, res) => { + const cookie = req.header('cookie'); + if (cookie === undefined) { + res.cookie('test', 1, { maxAge: 900000, httpOnly: true, secure: true }); + res.send('cookie set'); + } else { + res.send('cookie sent'); + } +}); + +let base = 'http://localhost'; +let server; +before(function listen(done) { + server = http.createServer(app); + server = server.listen(0, function listening() { + base += `:${server.address().port}`; + done(); + }); +}); + +const agent1 = request.agent(); +const agent2 = request.agent({ sendSecureCookie: true }); +const agent3 = request.agent(); + +describe('Secure cookie', () => { + it('Should receive a secure cookie', () => { + agent1.get(`${base}/`).then(res => { + res.should.have.status(200); + should.exist(res.headers['set-cookie']); + res.headers['set-cookie'][0].should.containEql('Secure'); + res.text.should.containEql('cookie set'); + }); + + agent2.get(`${base}/`).then(res => { + res.should.have.status(200); + should.exist(res.headers['set-cookie']); + res.headers['set-cookie'][0].should.containEql('Secure'); + res.text.should.containEql('cookie set'); + }); + + agent3.get(`${base}/`).then(res => { + res.should.have.status(200); + should.exist(res.headers['set-cookie']); + res.headers['set-cookie'][0].should.containEql('Secure'); + res.text.should.containEql('cookie set'); + }); + }); + + it('Should send secure cookie on configured agents', () => { + agent1 + .sendSecureCookie() + .get(`${base}/`) + .then(res => { + res.should.have.status(200); + should.not.exist(res.headers['set-cookie']); + res.text.should.containEql('cookie sent'); + }); + + agent2.get(`${base}/`).then(res => { + res.should.have.status(200); + should.not.exist(res.headers['set-cookie']); + res.text.should.containEql('cookie sent'); + }); + }); + + it('Should not send secure cookie on default agent', () => { + agent3.get(`${base}/`).then(res => { + res.should.have.status(200); + should.exist(res.headers['set-cookie']); + res.text.should.containEql('cookie set'); + }); + }); +});