From 237583244154de87b938a54e5aa07d0e996c118f Mon Sep 17 00:00:00 2001 From: LouisSung <37973545+LouisSung@users.noreply.github.com> Date: Wed, 6 Jan 2021 02:40:36 +0800 Subject: [PATCH 1/5] http_client: prevent domain fronting (Host mismatch) when using proxy --- lib/_http_client.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/lib/_http_client.js b/lib/_http_client.js index b81ffa1cefc573..bbca3c61676d2b 100644 --- a/lib/_http_client.js +++ b/lib/_http_client.js @@ -284,6 +284,14 @@ function ClientRequest(input, options, cb) { if (port && +port !== defaultPort) { hostHeader += ':' + port; } + + // Prevent potential domain fronting misjudgement when using proxy + // Overwrite the "default Host requst header" with the host of path + if (!this.path.startsWith('/')) { + hostHeader = new URL( + `http://${this.path.replace(/^.*:\/\//, '')}`).host; + } + this.setHeader('Host', hostHeader); } From e4ef0cd37de33cbfef249e4160cdf631989fb20c Mon Sep 17 00:00:00 2001 From: LouisSung <37973545+LouisSung@users.noreply.github.com> Date: Wed, 6 Jan 2021 08:08:27 +0800 Subject: [PATCH 2/5] fix: replace builtin modules with per_context/primordials --- lib/_http_client.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/_http_client.js b/lib/_http_client.js index bbca3c61676d2b..147d325ff3a9d7 100644 --- a/lib/_http_client.js +++ b/lib/_http_client.js @@ -36,6 +36,8 @@ const { StringPrototypeCharCodeAt, StringPrototypeIncludes, StringPrototypeIndexOf, + StringPrototypeReplace, + StringPrototypeStartsWith, StringPrototypeToUpperCase, Symbol, TypedArrayPrototypeSlice, @@ -287,9 +289,9 @@ function ClientRequest(input, options, cb) { // Prevent potential domain fronting misjudgement when using proxy // Overwrite the "default Host requst header" with the host of path - if (!this.path.startsWith('/')) { + if (!StringPrototypeStartsWith(this.path, '/')) { hostHeader = new URL( - `http://${this.path.replace(/^.*:\/\//, '')}`).host; + `http://${StringPrototypeReplace(this.path, /^.*:\/\//, '')}`).host; } this.setHeader('Host', hostHeader); From b216679b2bb06c4105a6f554cfa6de35a58a4ea9 Mon Sep 17 00:00:00 2001 From: LouisSung <37973545+LouisSung@users.noreply.github.com> Date: Wed, 6 Jan 2021 08:13:34 +0800 Subject: [PATCH 3/5] test: modify test case to match Host in header with destination --- test/parallel/test-tls-over-http-tunnel.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/parallel/test-tls-over-http-tunnel.js b/test/parallel/test-tls-over-http-tunnel.js index b26cf7872f6582..6fd552a1e30cc2 100644 --- a/test/parallel/test-tls-over-http-tunnel.js +++ b/test/parallel/test-tls-over-http-tunnel.js @@ -61,7 +61,8 @@ const proxy = net.createServer((clientSocket) => { `CONNECT localhost:${server.address().port} ` + 'HTTP/1.1\r\n' + 'Proxy-Connections: keep-alive\r\n' + - `Host: localhost:${proxy.address().port}\r\n` + + // Match header Host with destination Host + `Host: localhost:${server.address().port}\r\n` + 'Connection: close\r\n\r\n'); console.log('PROXY: got CONNECT request'); From 3919518031d6905be840fb79dd6e5bfda7addc81 Mon Sep 17 00:00:00 2001 From: LouisSung <37973545+LouisSung@users.noreply.github.com> Date: Sat, 9 Jan 2021 21:09:44 +0800 Subject: [PATCH 4/5] feat: adjust implementation to reuse IPv6 Host address check --- lib/_http_client.js | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/lib/_http_client.js b/lib/_http_client.js index 147d325ff3a9d7..acdc5d755f52e0 100644 --- a/lib/_http_client.js +++ b/lib/_http_client.js @@ -36,7 +36,7 @@ const { StringPrototypeCharCodeAt, StringPrototypeIncludes, StringPrototypeIndexOf, - StringPrototypeReplace, + StringPrototypeMatch, StringPrototypeStartsWith, StringPrototypeToUpperCase, Symbol, @@ -272,6 +272,17 @@ function ClientRequest(input, options, cb) { if (host && !this.getHeader('host') && setHost) { let hostHeader = host; + let hostHeaderPort = port; + + // Prevent potential domain fronting misjudgement when using proxy + // Overwrite the default Host request header with the host of path + if (!StringPrototypeStartsWith(this.path, '/')) { + const proxyURL = new URL(StringPrototypeMatch( + this.path, '^.+://') ? this.path : `http://${this.path}`); + hostHeader = proxyURL.hostname; + hostHeaderPort = proxyURL.port || ( + proxyURL.protocol === 'https:' ? '443' : '80'); + } // For the Host header, ensure that IPv6 addresses are enclosed // in square brackets, as defined by URI formatting @@ -283,17 +294,9 @@ function ClientRequest(input, options, cb) { hostHeader = `[${hostHeader}]`; } - if (port && +port !== defaultPort) { - hostHeader += ':' + port; + if (hostHeaderPort && +hostHeaderPort !== defaultPort) { + hostHeader += ':' + hostHeaderPort; } - - // Prevent potential domain fronting misjudgement when using proxy - // Overwrite the "default Host requst header" with the host of path - if (!StringPrototypeStartsWith(this.path, '/')) { - hostHeader = new URL( - `http://${StringPrototypeReplace(this.path, /^.*:\/\//, '')}`).host; - } - this.setHeader('Host', hostHeader); } From e99bd7039db611ec72e178f8c43cb547b2e0d28c Mon Sep 17 00:00:00 2001 From: LouisSung <37973545+LouisSung@users.noreply.github.com> Date: Mon, 18 Jan 2021 00:18:42 +0800 Subject: [PATCH 5/5] feat: apply suggestion to replace StringPrototypeMatch with RegExpPrototypeTest Co-authored-by: Antoine du Hamel --- lib/_http_client.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/lib/_http_client.js b/lib/_http_client.js index acdc5d755f52e0..4a9d199ba398c7 100644 --- a/lib/_http_client.js +++ b/lib/_http_client.js @@ -36,7 +36,6 @@ const { StringPrototypeCharCodeAt, StringPrototypeIncludes, StringPrototypeIndexOf, - StringPrototypeMatch, StringPrototypeStartsWith, StringPrototypeToUpperCase, Symbol, @@ -277,8 +276,8 @@ function ClientRequest(input, options, cb) { // Prevent potential domain fronting misjudgement when using proxy // Overwrite the default Host request header with the host of path if (!StringPrototypeStartsWith(this.path, '/')) { - const proxyURL = new URL(StringPrototypeMatch( - this.path, '^.+://') ? this.path : `http://${this.path}`); + const proxyURL = new URL(RegExpPrototypeTest( + /^[^:]+:\/\//, this.path) ? this.path : `http://${this.path}`); hostHeader = proxyURL.hostname; hostHeaderPort = proxyURL.port || ( proxyURL.protocol === 'https:' ? '443' : '80');