Skip to content

Commit c5bcf21

Browse files
feat: Add allowInsecureRedirect option
BREAKING CHANGE: The allowInsecureRedirect is `false` by default, which may cause issues if your usage relies on insecure redirects. For the former behavior, you can opt in to insecure redirects by setting the option to `true`, but it is not recommended. Co-authored-by: Szymon Drosdzol <[email protected]>
1 parent 0664780 commit c5bcf21

7 files changed

+26
-3
lines changed

README.md

+1
Original file line numberDiff line numberDiff line change
@@ -707,6 +707,7 @@ The first argument can be either a `url` or an `options` object. The only requir
707707
- `followOriginalHttpMethod` - by default we redirect to HTTP method GET. you can enable this property to redirect to the original HTTP method (default: `false`)
708708
- `maxRedirects` - the maximum number of redirects to follow (default: `10`)
709709
- `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.
710+
- `allowInsecureRedirect` - allows cross-protocol redirects (HTTP to HTTPS and vice versa). **Warning:** may lead to bypassing anti SSRF filters (default: `false`)
710711

711712
---
712713

lib/redirect.js

+5-1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ function Redirect (request) {
1414
this.redirects = []
1515
this.redirectsFollowed = 0
1616
this.removeRefererHeader = false
17+
this.allowInsecureRedirect = false
1718
}
1819

1920
Redirect.prototype.onRequest = function (options) {
@@ -40,6 +41,9 @@ Redirect.prototype.onRequest = function (options) {
4041
if (options.followOriginalHttpMethod !== undefined) {
4142
self.followOriginalHttpMethod = options.followOriginalHttpMethod
4243
}
44+
if (options.allowInsecureRedirect !== undefined) {
45+
self.allowInsecureRedirect = options.allowInsecureRedirect
46+
}
4347
}
4448

4549
Redirect.prototype.redirectTo = function (response) {
@@ -113,7 +117,7 @@ Redirect.prototype.onResponse = function (response, callback) {
113117
request.uri = url.parse(redirectTo)
114118

115119
// handle the case where we change protocol from https to http or vice versa
116-
if (request.uri.protocol !== uriPrev.protocol) {
120+
if (request.uri.protocol !== uriPrev.protocol && self.allowInsecureRedirect) {
117121
delete request.agent
118122
}
119123

tests/test-httpModule.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ function runTests (name, httpModules) {
7070
tape(name, function (t) {
7171
var toHttps = 'http://localhost:' + plainServer.port + '/to_https'
7272
var toPlain = 'https://localhost:' + httpsServer.port + '/to_plain'
73-
var options = { httpModules: httpModules, strictSSL: false }
73+
var options = { httpModules: httpModules, strictSSL: false, allowInsecureRedirect: true }
7474
var modulesTest = httpModules || {}
7575

7676
clearFauxRequests()

tests/test-redirect-auth.js

+1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ request = request.defaults({
1818
user: 'test',
1919
pass: 'testing'
2020
},
21+
allowInsecureRedirect: true,
2122
rejectUnauthorized: false
2223
})
2324

tests/test-redirect-complex.js

+1
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ tape('lots of redirects', function (t) {
6767
request({
6868
url: (i % 2 ? s.url : ss.url) + '/a',
6969
headers: { 'x-test-key': key },
70+
allowInsecureRedirect: true,
7071
rejectUnauthorized: false
7172
}, function (err, res, body) {
7273
t.equal(err, null)

tests/test-redirect.js

+14-1
Original file line numberDiff line numberDiff line change
@@ -445,7 +445,8 @@ tape('http to https redirect', function (t) {
445445
hits = {}
446446
request.get({
447447
uri: require('url').parse(s.url + '/ssl'),
448-
rejectUnauthorized: false
448+
rejectUnauthorized: false,
449+
allowInsecureRedirect: true
449450
}, function (err, res, body) {
450451
t.equal(err, null)
451452
t.equal(res.statusCode, 200)
@@ -454,6 +455,18 @@ tape('http to https redirect', function (t) {
454455
})
455456
})
456457

458+
tape('http to https redirect should fail without the explicit "allowInsecureRedirect" option', function (t) {
459+
hits = {}
460+
request.get({
461+
uri: require('url').parse(s.url + '/ssl'),
462+
rejectUnauthorized: false
463+
}, function (err, res, body) {
464+
t.notEqual(err, null)
465+
t.equal(err.code, 'ERR_INVALID_PROTOCOL', 'Failed to cross-protocol redirect')
466+
t.end()
467+
})
468+
})
469+
457470
tape('should have referer header by default when following redirect', function (t) {
458471
request.post({
459472
uri: s.url + '/temp',

tests/test-tunnel.js

+3
Original file line numberDiff line numberDiff line change
@@ -356,6 +356,7 @@ function addTests () {
356356
'https->http over http, tunnel=true',
357357
{
358358
url: ss.url + '/redirect/http',
359+
allowInsecureRedirect: true,
359360
proxy: s.url,
360361
tunnel: true
361362
},
@@ -372,6 +373,7 @@ function addTests () {
372373
'https->http over http, tunnel=false',
373374
{
374375
url: ss.url + '/redirect/http',
376+
allowInsecureRedirect: true,
375377
proxy: s.url,
376378
tunnel: false
377379
},
@@ -388,6 +390,7 @@ function addTests () {
388390
'https->http over http, tunnel=default',
389391
{
390392
url: ss.url + '/redirect/http',
393+
allowInsecureRedirect: true,
391394
proxy: s.url
392395
},
393396
[

0 commit comments

Comments
 (0)