diff --git a/README.md b/README.md index dcc0897f9..47bc4da72 100644 --- a/README.md +++ b/README.md @@ -707,6 +707,7 @@ The first argument can be either a `url` or an `options` object. The only requir - `followOriginalHttpMethod` - by default we redirect to HTTP method GET. you can enable this property to redirect to the original HTTP method (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 500252cec..4634a6390 100644 --- a/lib/redirect.js +++ b/lib/redirect.js @@ -14,6 +14,7 @@ function Redirect (request) { this.redirects = [] this.redirectsFollowed = 0 this.removeRefererHeader = false + this.allowInsecureRedirect = false } Redirect.prototype.onRequest = function (options) { @@ -40,6 +41,9 @@ Redirect.prototype.onRequest = function (options) { if (options.followOriginalHttpMethod !== undefined) { self.followOriginalHttpMethod = options.followOriginalHttpMethod } + if (options.allowInsecureRedirect !== undefined) { + self.allowInsecureRedirect = options.allowInsecureRedirect + } } Redirect.prototype.redirectTo = function (response) { @@ -113,7 +117,7 @@ Redirect.prototype.onResponse = function (response, callback) { request.uri = url.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.js b/tests/test-redirect.js index d2ad88051..b3b581c21 100644 --- a/tests/test-redirect.js +++ b/tests/test-redirect.js @@ -445,7 +445,8 @@ tape('http to https redirect', function (t) { hits = {} request.get({ uri: require('url').parse(s.url + '/ssl'), - rejectUnauthorized: false + rejectUnauthorized: false, + allowInsecureRedirect: true }, function (err, res, body) { t.equal(err, null) t.equal(res.statusCode, 200) @@ -454,6 +455,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.code, 'ERR_INVALID_PROTOCOL', '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.js b/tests/test-tunnel.js index b78649bc8..10c045c55 100644 --- a/tests/test-tunnel.js +++ b/tests/test-tunnel.js @@ -356,6 +356,7 @@ function addTests () { 'https->http over http, tunnel=true', { url: ss.url + '/redirect/http', + allowInsecureRedirect: true, proxy: s.url, tunnel: true }, @@ -372,6 +373,7 @@ function addTests () { 'https->http over http, tunnel=false', { url: ss.url + '/redirect/http', + allowInsecureRedirect: true, proxy: s.url, tunnel: false }, @@ -388,6 +390,7 @@ function addTests () { 'https->http over http, tunnel=default', { url: ss.url + '/redirect/http', + allowInsecureRedirect: true, proxy: s.url }, [