golang / go

The Go programming language
https://go.dev
BSD 3-Clause "New" or "Revised" License
123.66k stars 17.62k forks source link

net/http: discrepancy in CRLF termination in http messages #53186

Open blessingcharles opened 2 years ago

blessingcharles commented 2 years ago

What version of Go are you using (go version)?

      1.18.2

Does this issue reproduce with the latest release?

yes

What did you do?

  1. While fuzzing for analyzing the discrepancies in the parsing behavior of various servers , the termination chunk of 0\n\r\n was accepted and most servers like nodejs and actix replied witj 400 request . According to RFC the termination chunk must be 0CRLFCRLF ie 0\r\n\r\n but golang accept 0\n\r\n . RFC chunked termination

  2. Every header , requestline and body delimiting end should be CRLF according to RFC ABNF grammar but golang accepts LF alone as a valid request .RFC 7230

POC
echo -ne "GET / HTTP/1.1\nHost: localhost\nTransfer-encoding: chunked\n\n2\naa\r\n0\n\r\n" | nc localhost 8004 

What did you expect to see?

  1. Correctly parse the CRLF termination according to RFC standards and other servers

What operating system and processor architecture are you using (go env)?

Environment

FROM golang
USER root
WORKDIR /go/src/app
COPY server.go /go/src/app/server.go
RUN go mod init
RUN go build .
CMD ["./app"]

Code used

package main

import (
    "fmt"
    "io/ioutil"
    "net/http"
    "log"
)

func main() {
    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {

        b, err := ioutil.ReadAll(r.Body)
        if err != nil {
            w.Write([]byte(fmt.Sprintf("Got error while reading body: %v", err)))
            return
        }
        fmt.Printf("Body length: %d Body: %q", len(b), b)
        w.Write([]byte(fmt.Sprintf("Body length: %d Body: %q", len(b), b)))
    })

    fmt.Printf("Server Starting at port 80") 
    if err := http.ListenAndServe(":80", nil) ; err != nil {
        log.Fatal(err)
    }
}
neild commented 2 years ago

RFC 7230 section 3.5 explicitly allows for accepting a bare LF:

Although the line terminator for the start-line and header fields is the sequence CRLF, a recipient MAY recognize a single LF as a line terminator and ignore any preceding CR.

Perhaps we should be stricter, but the current behavior is within the parameters permitted by RFC 7230.