diff --git a/README.md b/README.md index ab099854b..b4883395c 100644 --- a/README.md +++ b/README.md @@ -941,6 +941,7 @@ The first argument can be either a `url` or an `options` object. The only requir - `followAuthorizationHeader` - retain `authorization` header when a redirect happens to a different hostname (default: `false`) - `maxRedirects` - the maximum number of redirects to follow (default: `10`) - `removeRefererHeader` - removes the referer header when a redirect happens (default: `false`). **Note:** if true, referer header set in the initial request is preserved during redirect chain. +- `allowInsecureRedirect` - allows cross-protocol redirects (HTTP to HTTPS and vice versa). **Warning:** may lead to bypassing anti SSRF filters (default: `false`) --- diff --git a/lib/redirect.js b/lib/redirect.js index 47bf0ad28..a27026869 100644 --- a/lib/redirect.js +++ b/lib/redirect.js @@ -15,6 +15,7 @@ function Redirect (request) { this.redirects = [] this.redirectsFollowed = 0 this.removeRefererHeader = false + this.allowInsecureRedirect = false } Redirect.prototype.onRequest = function (options) { @@ -44,6 +45,9 @@ Redirect.prototype.onRequest = function (options) { if (options.followAuthorizationHeader !== undefined) { self.followAuthorizationHeader = options.followAuthorizationHeader } + if (options.allowInsecureRedirect !== undefined) { + self.allowInsecureRedirect = options.allowInsecureRedirect + } } Redirect.prototype.redirectTo = function (response) { @@ -122,7 +126,7 @@ Redirect.prototype.onResponse = function (response, callback) { request.uri = urlParser.parse(redirectTo) // handle the case where we change protocol from https to http or vice versa - if (request.uri.protocol !== uriPrev.protocol) { + if (request.uri.protocol !== uriPrev.protocol && self.allowInsecureRedirect) { delete request.agent } diff --git a/tests/test-httpModule.js b/tests/test-httpModule.js index 4d4e236bf..f12382fe6 100644 --- a/tests/test-httpModule.js +++ b/tests/test-httpModule.js @@ -70,7 +70,7 @@ function runTests (name, httpModules) { tape(name, function (t) { var toHttps = 'http://localhost:' + plainServer.port + '/to_https' var toPlain = 'https://localhost:' + httpsServer.port + '/to_plain' - var options = { httpModules: httpModules, strictSSL: false } + var options = { httpModules: httpModules, strictSSL: false, allowInsecureRedirect: true } var modulesTest = httpModules || {} clearFauxRequests() diff --git a/tests/test-redirect-auth.js b/tests/test-redirect-auth.js index 7aef6edcc..93b606537 100644 --- a/tests/test-redirect-auth.js +++ b/tests/test-redirect-auth.js @@ -18,6 +18,7 @@ request = request.defaults({ user: 'test', pass: 'testing' }, + allowInsecureRedirect: true, rejectUnauthorized: false }) diff --git a/tests/test-redirect-complex.js b/tests/test-redirect-complex.js index 072b5986c..2ba3a65ff 100644 --- a/tests/test-redirect-complex.js +++ b/tests/test-redirect-complex.js @@ -67,6 +67,7 @@ tape('lots of redirects', function (t) { request({ url: (i % 2 ? s.url : ss.url) + '/a', headers: { 'x-test-key': key }, + allowInsecureRedirect: true, rejectUnauthorized: false }, function (err, res, body) { t.equal(err, null) diff --git a/tests/test-redirect-multi-version.js b/tests/test-redirect-multi-version.js index 64bc225cc..bd19972a9 100644 --- a/tests/test-redirect-multi-version.js +++ b/tests/test-redirect-multi-version.js @@ -75,7 +75,7 @@ tape('setup', function (t) { }) tape('HTTP to HTTP2', function (t) { - var options = { strictSSL: false, protocolVersion: 'auto' } + var options = { strictSSL: false, protocolVersion: 'auto', allowInsecureRedirect: true } request('http://localhost:' + plainServer.port + '/redir/http2', options, function (err, res, body) { t.equal(err, null) t.equal(body, 'http2') @@ -97,7 +97,7 @@ tape('HTTPS to HTTP2', function (t) { }) tape('HTTP2 to HTTP', function (t) { - var options = { strictSSL: false, protocolVersion: 'auto' } + var options = { strictSSL: false, protocolVersion: 'auto', allowInsecureRedirect: true } request('https://localhost:' + http2Server.port + '/redir', options, function (err, res, body) { t.equal(err, null) t.equal(body, 'plain') diff --git a/tests/test-redirect.js b/tests/test-redirect.js index a0fdb19cc..1149b2a20 100644 --- a/tests/test-redirect.js +++ b/tests/test-redirect.js @@ -533,6 +533,7 @@ tape('http to https redirect', function (t) { hits = {} request.get({ uri: require('url').parse(s.url + '/ssl'), + allowInsecureRedirect: true, rejectUnauthorized: false }, function (err, res, body) { t.equal(err, null) @@ -542,6 +543,18 @@ tape('http to https redirect', function (t) { }) }) +tape('http to https redirect should fail without the explicit "allowInsecureRedirect" option', function (t) { + hits = {} + request.get({ + uri: require('url').parse(s.url + '/ssl'), + rejectUnauthorized: false + }, function (err, res, body) { + t.notEqual(err, null) + t.equal(err.message, 'Protocol "https:" not supported. Expected "http:"', 'Failed to cross-protocol redirect') + t.end() + }) +}) + tape('should have referer header by default when following redirect', function (t) { request.post({ uri: s.url + '/temp', diff --git a/tests/test-tunnel-response.js b/tests/test-tunnel-response.js index 1224a0494..6f0902114 100644 --- a/tests/test-tunnel-response.js +++ b/tests/test-tunnel-response.js @@ -289,6 +289,7 @@ function addTests () { runTest('https->http over http, tunnel=default, with proxy response', { url: ss.url + '/redirect/http', + allowInsecureRedirect: true, proxy: s.url }, [ 'http connect to localhost:' + ss.port, diff --git a/tests/test-tunnel.js b/tests/test-tunnel.js index 3aee30528..5e093025f 100644 --- a/tests/test-tunnel.js +++ b/tests/test-tunnel.js @@ -394,6 +394,7 @@ function addTests () { runTest('https->http over http, tunnel=default', { url: ss.url + '/redirect/http', + allowInsecureRedirect: true, proxy: s.url }, [ 'http connect to localhost:' + ss.port, diff --git a/tests/test-verbose-auto-http2.js b/tests/test-verbose-auto-http2.js index 13d3addd4..dad443533 100644 --- a/tests/test-verbose-auto-http2.js +++ b/tests/test-verbose-auto-http2.js @@ -90,6 +90,7 @@ tape('HTTP: verbose=true', function (t) { tape('HTTP: redirect(HTTPS) + verbose=true', function (t) { var options = { verbose: true, + allowInsecureRedirect: true, strictSSL: false, protocolVersion: 'auto' } @@ -153,6 +154,7 @@ tape('HTTPS: verbose=true', function (t) { tape('HTTPS: redirect(HTTP) + verbose=true', function (t) { var options = { verbose: true, + allowInsecureRedirect: true, strictSSL: false, protocolVersion: 'auto' } diff --git a/tests/test-verbose-http2.js b/tests/test-verbose-http2.js index 40f14476a..fcf68ac5e 100644 --- a/tests/test-verbose-http2.js +++ b/tests/test-verbose-http2.js @@ -99,6 +99,7 @@ tape('HTTP: verbose=true', function (t) { tape('HTTP: redirect(HTTPS) + verbose=true', function (t) { var options = { verbose: true, + allowInsecureRedirect: true, strictSSL: false, protocolVersion: 'http2' } @@ -191,6 +192,7 @@ tape('HTTPS Gzip: verbose=true', function (t) { tape('HTTPS: redirect(HTTP) + verbose=true', function (t) { var options = { verbose: true, + allowInsecureRedirect: true, strictSSL: false, protocolVersion: 'http2' } diff --git a/tests/test-verbose.js b/tests/test-verbose.js index 741ae14d6..eaa984d26 100644 --- a/tests/test-verbose.js +++ b/tests/test-verbose.js @@ -98,6 +98,7 @@ tape('HTTP: verbose=true', function (t) { tape('HTTP: redirect(HTTPS) + verbose=true', function (t) { var options = { verbose: true, + allowInsecureRedirect: true, strictSSL: false } @@ -189,6 +190,7 @@ tape('HTTPS Gzip: verbose=true', function (t) { tape('HTTPS: redirect(HTTP) + verbose=true', function (t) { var options = { verbose: true, + allowInsecureRedirect: true, strictSSL: false }