From e4e7807d240eb62e1d4a73eec2706975c8cc847b Mon Sep 17 00:00:00 2001 From: Filippo Valsorda Date: Wed, 9 Jun 2021 07:43:57 -0400 Subject: net/http: add AllowQuerySemicolons Fixes #45973 Change-Id: I6cbe05f5d1d3c324900c74314b0ea0e12524d7f2 Reviewed-on: https://go-review.googlesource.com/c/go/+/326309 Run-TryBot: Filippo Valsorda Reviewed-by: Katie Hockman Trust: Katie Hockman Trust: Filippo Valsorda TryBot-Result: Go Bot --- src/net/http/server.go | 45 +++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 41 insertions(+), 4 deletions(-) (limited to 'src/net/http/server.go') diff --git a/src/net/http/server.go b/src/net/http/server.go index 8a1847e67a..50fab4520d 100644 --- a/src/net/http/server.go +++ b/src/net/http/server.go @@ -2862,12 +2862,49 @@ func (sh serverHandler) ServeHTTP(rw ResponseWriter, req *Request) { if req.RequestURI == "*" && req.Method == "OPTIONS" { handler = globalOptionsHandler{} } - handler.ServeHTTP(rw, req) + if req.URL != nil && strings.Contains(req.URL.RawQuery, ";") { - // TODO(filippo): update this not to log if the special - // semicolon handler was called. - sh.srv.logf("http: URL query contains semicolon, which is no longer a supported separator; parts of the query may be stripped when parsed; see golang.org/issue/25192") + var allowQuerySemicolonsInUse int32 + req = req.WithContext(context.WithValue(req.Context(), silenceSemWarnContextKey, func() { + atomic.StoreInt32(&allowQuerySemicolonsInUse, 1) + })) + defer func() { + if atomic.LoadInt32(&allowQuerySemicolonsInUse) == 0 { + sh.srv.logf("http: URL query contains semicolon, which is no longer a supported separator; parts of the query may be stripped when parsed; see golang.org/issue/25192") + } + }() } + + handler.ServeHTTP(rw, req) +} + +var silenceSemWarnContextKey = &contextKey{"silence-semicolons"} + +// AllowQuerySemicolons returns a handler that serves requests by converting any +// unescaped semicolons in the URL query to ampersands, and invoking the handler h. +// +// This restores the pre-Go 1.17 behavior of splitting query parameters on both +// semicolons and ampersands. (See golang.org/issue/25192). Note that this +// behavior doesn't match that of many proxies, and the mismatch can lead to +// security issues. +// +// AllowQuerySemicolons should be invoked before Request.ParseForm is called. +func AllowQuerySemicolons(h Handler) Handler { + return HandlerFunc(func(w ResponseWriter, r *Request) { + if silenceSemicolonsWarning, ok := r.Context().Value(silenceSemWarnContextKey).(func()); ok { + silenceSemicolonsWarning() + } + if strings.Contains(r.URL.RawQuery, ";") { + r2 := new(Request) + *r2 = *r + r2.URL = new(url.URL) + *r2.URL = *r.URL + r2.URL.RawQuery = strings.ReplaceAll(r.URL.RawQuery, ";", "&") + h.ServeHTTP(w, r2) + } else { + h.ServeHTTP(w, r) + } + }) } // ListenAndServe listens on the TCP network address srv.Addr and then -- cgit v1.3-5-g9baa