Skip to content

Commit c08a5fa

Browse files
neildgopherbot
authored andcommitted
[release-branch.go1.19] net/http: permit requests with invalid Host headers
Historically, the Transport has silently truncated invalid Host headers at the first '/' or ' ' character. CL 506996 changed this behavior to reject invalid Host headers entirely. Unfortunately, Docker appears to rely on the previous behavior. When sending a HTTP/1 request with an invalid Host, send an empty Host header. This is safer than truncation: If you care about the Host, then you should get the one you set; if you don't care, then an empty Host should be fine. Continue to fully validate Host headers sent to a proxy, since proxies generally can't productively forward requests without a Host. For #60374 Fixes #61431 Fixes #61825 Change-Id: If170c7dd860aa20eb58fe32990fc93af832742b6 Reviewed-on: https://go-review.googlesource.com/c/go/+/511155 TryBot-Result: Gopher Robot <[email protected]> Reviewed-by: Roland Shoemaker <[email protected]> Run-TryBot: Damien Neil <[email protected]> (cherry picked from commit b9153f6) Reviewed-on: https://go-review.googlesource.com/c/go/+/518855 Auto-Submit: Dmitri Shuralyov <[email protected]> Run-TryBot: Roland Shoemaker <[email protected]> Reviewed-by: Russ Cox <[email protected]>
1 parent 2f498f2 commit c08a5fa

File tree

2 files changed

+34
-6
lines changed

2 files changed

+34
-6
lines changed

src/net/http/request.go

+22-1
Original file line numberDiff line numberDiff line change
@@ -582,8 +582,29 @@ func (r *Request) write(w io.Writer, usingProxy bool, extraHeaders Header, waitF
582582
if err != nil {
583583
return err
584584
}
585+
// Validate that the Host header is a valid header in general,
586+
// but don't validate the host itself. This is sufficient to avoid
587+
// header or request smuggling via the Host field.
588+
// The server can (and will, if it's a net/http server) reject
589+
// the request if it doesn't consider the host valid.
585590
if !httpguts.ValidHostHeader(host) {
586-
return errors.New("http: invalid Host header")
591+
// Historically, we would truncate the Host header after '/' or ' '.
592+
// Some users have relied on this truncation to convert a network
593+
// address such as Unix domain socket path into a valid, ignored
594+
// Host header (see https://go.dev/issue/61431).
595+
//
596+
// We don't preserve the truncation, because sending an altered
597+
// header field opens a smuggling vector. Instead, zero out the
598+
// Host header entirely if it isn't valid. (An empty Host is valid;
599+
// see RFC 9112 Section 3.2.)
600+
//
601+
// Return an error if we're sending to a proxy, since the proxy
602+
// probably can't do anything useful with an empty Host header.
603+
if !usingProxy {
604+
host = ""
605+
} else {
606+
return errors.New("http: invalid Host header")
607+
}
587608
}
588609

589610
// According to RFC 6874, an HTTP client, proxy, or other

src/net/http/request_test.go

+12-5
Original file line numberDiff line numberDiff line change
@@ -770,16 +770,23 @@ func TestRequestWriteBufferedWriter(t *testing.T) {
770770
}
771771
}
772772

773-
func TestRequestBadHost(t *testing.T) {
773+
func TestRequestBadHostHeader(t *testing.T) {
774774
got := []string{}
775775
req, err := NewRequest("GET", "http://foo/after", nil)
776776
if err != nil {
777777
t.Fatal(err)
778778
}
779-
req.Host = "foo.com with spaces"
780-
req.URL.Host = "foo.com with spaces"
781-
if err := req.Write(logWrites{t, &got}); err == nil {
782-
t.Errorf("Writing request with invalid Host: succeded, want error")
779+
req.Host = "foo.com\nnewline"
780+
req.URL.Host = "foo.com\nnewline"
781+
req.Write(logWrites{t, &got})
782+
want := []string{
783+
"GET /after HTTP/1.1\r\n",
784+
"Host: \r\n",
785+
"User-Agent: " + DefaultUserAgent + "\r\n",
786+
"\r\n",
787+
}
788+
if !reflect.DeepEqual(got, want) {
789+
t.Errorf("Writes = %q\n Want = %q", got, want)
783790
}
784791
}
785792

0 commit comments

Comments
 (0)