if err != nil {
return err
}
+ // Validate that the Host header is a valid header in general,
+ // but don't validate the host itself. This is sufficient to avoid
+ // header or request smuggling via the Host field.
+ // The server can (and will, if it's a net/http server) reject
+ // the request if it doesn't consider the host valid.
if !httpguts.ValidHostHeader(host) {
- return errors.New("http: invalid Host header")
+ // Historically, we would truncate the Host header after '/' or ' '.
+ // Some users have relied on this truncation to convert a network
+ // address such as Unix domain socket path into a valid, ignored
+ // Host header (see https://go.dev/issue/61431).
+ //
+ // We don't preserve the truncation, because sending an altered
+ // header field opens a smuggling vector. Instead, zero out the
+ // Host header entirely if it isn't valid. (An empty Host is valid;
+ // see RFC 9112 Section 3.2.)
+ //
+ // Return an error if we're sending to a proxy, since the proxy
+ // probably can't do anything useful with an empty Host header.
+ if !usingProxy {
+ host = ""
+ } else {
+ return errors.New("http: invalid Host header")
+ }
}
// According to RFC 6874, an HTTP client, proxy, or other
}
}
-func TestRequestBadHost(t *testing.T) {
+func TestRequestBadHostHeader(t *testing.T) {
got := []string{}
req, err := NewRequest("GET", "http://foo/after", nil)
if err != nil {
t.Fatal(err)
}
- req.Host = "foo.com with spaces"
- req.URL.Host = "foo.com with spaces"
- if err := req.Write(logWrites{t, &got}); err == nil {
- t.Errorf("Writing request with invalid Host: succeded, want error")
+ req.Host = "foo.com\nnewline"
+ req.URL.Host = "foo.com\nnewline"
+ req.Write(logWrites{t, &got})
+ want := []string{
+ "GET /after HTTP/1.1\r\n",
+ "Host: \r\n",
+ "User-Agent: " + DefaultUserAgent + "\r\n",
+ "\r\n",
+ }
+ if !reflect.DeepEqual(got, want) {
+ t.Errorf("Writes = %q\n Want = %q", got, want)
}
}