From 9d2c73a9fd69e45876509bb3bdb2af99bf77da1e Mon Sep 17 00:00:00 2001 From: Damien Neil Date: Thu, 22 Sep 2022 13:32:00 -0700 Subject: [release-branch.go1.18] net/http/httputil: avoid query parameter smuggling Query parameter smuggling occurs when a proxy's interpretation of query parameters differs from that of a downstream server. Change ReverseProxy to avoid forwarding ignored query parameters. Remove unparsable query parameters from the outbound request * if req.Form != nil after calling ReverseProxy.Director; and * before calling ReverseProxy.Rewrite. This change preserves the existing behavior of forwarding the raw query untouched if a Director hook does not parse the query by calling Request.ParseForm (possibly indirectly). Fixes #55842 For #54663 For CVE-2022-2880 Change-Id: If1621f6b0e73a49d79059dae9e6b256e0ff18ca9 Reviewed-on: https://go-review.googlesource.com/c/go/+/432976 Reviewed-by: Roland Shoemaker Reviewed-by: Brad Fitzpatrick TryBot-Result: Gopher Robot Run-TryBot: Damien Neil (cherry picked from commit 7c84234142149bd24a4096c6cab691d3593f3431) Reviewed-on: https://go-review.googlesource.com/c/go/+/433695 Reviewed-by: Dmitri Shuralyov Reviewed-by: Dmitri Shuralyov --- src/net/http/httputil/reverseproxy.go | 36 +++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) (limited to 'src/net/http/httputil/reverseproxy.go') diff --git a/src/net/http/httputil/reverseproxy.go b/src/net/http/httputil/reverseproxy.go index 319e2a3f3f..0abc32b833 100644 --- a/src/net/http/httputil/reverseproxy.go +++ b/src/net/http/httputil/reverseproxy.go @@ -250,6 +250,9 @@ func (p *ReverseProxy) ServeHTTP(rw http.ResponseWriter, req *http.Request) { } p.Director(outreq) + if outreq.Form != nil { + outreq.URL.RawQuery = cleanQueryParams(outreq.URL.RawQuery) + } outreq.Close = false reqUpType := upgradeType(outreq.Header) @@ -629,3 +632,36 @@ func (c switchProtocolCopier) copyToBackend(errc chan<- error) { _, err := io.Copy(c.backend, c.user) errc <- err } + +func cleanQueryParams(s string) string { + reencode := func(s string) string { + v, _ := url.ParseQuery(s) + return v.Encode() + } + for i := 0; i < len(s); { + switch s[i] { + case ';': + return reencode(s) + case '%': + if i+2 >= len(s) || !ishex(s[i+1]) || !ishex(s[i+2]) { + return reencode(s) + } + i += 3 + default: + i++ + } + } + return s +} + +func ishex(c byte) bool { + switch { + case '0' <= c && c <= '9': + return true + case 'a' <= c && c <= 'f': + return true + case 'A' <= c && c <= 'F': + return true + } + return false +} -- cgit v1.3