Skip to content

Commit da4d763

Browse files
wesleytoddjonchurchblakeembrey
committed
Improved fix for open redirect allow list bypass
Co-authored-by: Jon Church <[email protected]> Co-authored-by: Blake Embrey <[email protected]>
1 parent 4f0f6cc commit da4d763

File tree

3 files changed

+280
-63
lines changed

3 files changed

+280
-63
lines changed

History.md

+5
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
unreleased
2+
==========
3+
4+
* Improved fix for open redirect allow list bypass
5+
16
4.19.1 / 2024-03-20
27
==========
38

lib/response.js

+11-20
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,6 @@ var extname = path.extname;
3434
var mime = send.mime;
3535
var resolve = path.resolve;
3636
var vary = require('vary');
37-
var urlParse = require('url').parse;
3837

3938
/**
4039
* Response prototype.
@@ -56,6 +55,7 @@ module.exports = res
5655
*/
5756

5857
var charsetRegExp = /;\s*charset\s*=/;
58+
var schemaAndHostRegExp = /^(?:[a-zA-Z][a-zA-Z0-9+.-]*:)?\/\/[^\\\/\?]+/;
5959

6060
/**
6161
* Set status `code`.
@@ -905,32 +905,23 @@ res.cookie = function (name, value, options) {
905905
*/
906906

907907
res.location = function location(url) {
908-
var loc = String(url);
908+
var loc;
909909

910910
// "back" is an alias for the referrer
911911
if (url === 'back') {
912912
loc = this.req.get('Referrer') || '/';
913+
} else {
914+
loc = String(url);
913915
}
914916

915-
var lowerLoc = loc.toLowerCase();
916-
var encodedUrl = encodeUrl(loc);
917-
if (lowerLoc.indexOf('https://') === 0 || lowerLoc.indexOf('http://') === 0) {
918-
try {
919-
var parsedUrl = urlParse(loc);
920-
var parsedEncodedUrl = urlParse(encodedUrl);
921-
// Because this can encode the host, check that we did not change the host
922-
if (parsedUrl.host !== parsedEncodedUrl.host) {
923-
// If the host changes after encodeUrl, return the original url
924-
return this.set('Location', loc);
925-
}
926-
} catch (e) {
927-
// If parse fails, return the original url
928-
return this.set('Location', loc);
929-
}
930-
}
917+
var m = schemaAndHostRegExp.exec(loc);
918+
var pos = m ? m[0].length + 1 : 0;
919+
920+
// Only encode after host to avoid invalid encoding which can introduce
921+
// vulnerabilities (e.g. `\\` to `%5C`).
922+
loc = loc.slice(0, pos) + encodeUrl(loc.slice(pos));
931923

932-
// set location
933-
return this.set('Location', encodedUrl);
924+
return this.set('Location', loc);
934925
};
935926

936927
/**

0 commit comments

Comments
 (0)