net/http: make ReadRequest return an error when requests have multiple Host headers

Fixes #45513

Change-Id: I59e717a4bbd3e71320deff519e4f9587ee5c8756
Reviewed-on: https://go-review.googlesource.com/c/go/+/308952
Trust: Damien Neil <dneil@google.com>
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
This commit is contained in:
ian woolf 2021-04-12 17:19:03 +08:00 committed by Brad Fitzpatrick
parent 2f0e5bf907
commit acb189ea59
3 changed files with 16 additions and 15 deletions

View file

@ -1010,16 +1010,16 @@ func putTextprotoReader(r *textproto.Reader) {
// requests and handle them via the Handler interface. ReadRequest // requests and handle them via the Handler interface. ReadRequest
// only supports HTTP/1.x requests. For HTTP/2, use golang.org/x/net/http2. // only supports HTTP/1.x requests. For HTTP/2, use golang.org/x/net/http2.
func ReadRequest(b *bufio.Reader) (*Request, error) { func ReadRequest(b *bufio.Reader) (*Request, error) {
return readRequest(b, deleteHostHeader) req, err := readRequest(b)
if err != nil {
return nil, err
}
delete(req.Header, "Host")
return req, err
} }
// Constants for readRequest's deleteHostHeader parameter. func readRequest(b *bufio.Reader) (req *Request, err error) {
const (
deleteHostHeader = true
keepHostHeader = false
)
func readRequest(b *bufio.Reader, deleteHostHeader bool) (req *Request, err error) {
tp := newTextprotoReader(b) tp := newTextprotoReader(b)
req = new(Request) req = new(Request)
@ -1077,6 +1077,9 @@ func readRequest(b *bufio.Reader, deleteHostHeader bool) (req *Request, err erro
return nil, err return nil, err
} }
req.Header = Header(mimeHeader) req.Header = Header(mimeHeader)
if len(req.Header["Host"]) > 1 {
return nil, fmt.Errorf("too many Host headers")
}
// RFC 7230, section 5.3: Must treat // RFC 7230, section 5.3: Must treat
// GET /index.html HTTP/1.1 // GET /index.html HTTP/1.1
@ -1089,9 +1092,6 @@ func readRequest(b *bufio.Reader, deleteHostHeader bool) (req *Request, err erro
if req.Host == "" { if req.Host == "" {
req.Host = req.Header.get("Host") req.Host = req.Header.get("Host")
} }
if deleteHostHeader {
delete(req.Header, "Host")
}
fixPragmaCacheControl(req.Header) fixPragmaCacheControl(req.Header)

View file

@ -469,6 +469,10 @@ var readRequestErrorTests = []struct {
in: "HEAD / HTTP/1.1\r\nContent-Length:0\r\nContent-Length: 0\r\n\r\n", in: "HEAD / HTTP/1.1\r\nContent-Length:0\r\nContent-Length: 0\r\n\r\n",
header: Header{"Content-Length": {"0"}}, header: Header{"Content-Length": {"0"}},
}, },
11: {
in: "HEAD / HTTP/1.1\r\nHost: foo\r\nHost: bar\r\n\r\n\r\n\r\n",
err: "too many Host headers",
},
} }
func TestReadRequestErrors(t *testing.T) { func TestReadRequestErrors(t *testing.T) {

View file

@ -983,7 +983,7 @@ func (c *conn) readRequest(ctx context.Context) (w *response, err error) {
peek, _ := c.bufr.Peek(4) // ReadRequest will get err below peek, _ := c.bufr.Peek(4) // ReadRequest will get err below
c.bufr.Discard(numLeadingCRorLF(peek)) c.bufr.Discard(numLeadingCRorLF(peek))
} }
req, err := readRequest(c.bufr, keepHostHeader) req, err := readRequest(c.bufr)
if err != nil { if err != nil {
if c.r.hitReadLimit() { if c.r.hitReadLimit() {
return nil, errTooLarge return nil, errTooLarge
@ -1003,9 +1003,6 @@ func (c *conn) readRequest(ctx context.Context) (w *response, err error) {
if req.ProtoAtLeast(1, 1) && (!haveHost || len(hosts) == 0) && !isH2Upgrade && req.Method != "CONNECT" { if req.ProtoAtLeast(1, 1) && (!haveHost || len(hosts) == 0) && !isH2Upgrade && req.Method != "CONNECT" {
return nil, badRequestError("missing required Host header") return nil, badRequestError("missing required Host header")
} }
if len(hosts) > 1 {
return nil, badRequestError("too many Host headers")
}
if len(hosts) == 1 && !httpguts.ValidHostHeader(hosts[0]) { if len(hosts) == 1 && !httpguts.ValidHostHeader(hosts[0]) {
return nil, badRequestError("malformed Host header") return nil, badRequestError("malformed Host header")
} }