go version)?$ go version go version go1.14.2 linux/amd64
Yes
go env)?go env Output
$ go env
GO111MODULE="on"
GOARCH="amd64"
GOBIN=""
GOCACHE="/home/juliens/.cache/go-build"
GOENV="/home/juliens/.config/go/env"
GOEXE=""
GOFLAGS=""
GOHOSTARCH="amd64"
GOHOSTOS="linux"
GOINSECURE=""
GONOPROXY=""
GOOS="linux"
GOPATH="/home/juliens/dev/go"
GOPRIVATE=""
GOPROXY="direct"
GOROOT="/home/juliens/go-current"
GOSUMDB="sum.golang.org"
GOTMPDIR=""
GOTOOLDIR="/home/juliens/go-current/pkg/tool/linux_amd64"
GCCGO="gccgo"
AR="ar"
CC="gcc"
CXX="g++"
CGO_ENABLED="1"
GOMOD="/dev/null"
CGO_CFLAGS="-g -O2"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-g -O2"
CGO_FFLAGS="-g -O2"
CGO_LDFLAGS="-g -O2"
PKG_CONFIG="pkg-config"
GOGCCFLAGS="-fPIC -m64 -pthread -fmessage-length=0 -fdebug-prefix-map=/tmp/go-build262375258=/tmp/go-build -gno-record-gcc-switches"
I launch a server using
http.ListenAndServe(":8080", http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
fmt.Println(req.ContentLength)
}))
and after that, I make a call with a Content-Length: +3
echo -ne "POST / HTTP/1.1\r\nContent-Length: +3\r\nHost: 127.0.0.1\r\n\r\naaa\r\n" | nc 127.0.0.1 8083
A bad request ( because of the RFC https://tools.ietf.org/html/rfc2616#section-14.13 )
A valid request, the Content-Length is parsed as just 3
For what I saw, it's because the Content-Length is parsed by using strconv.ParseInt and so +3 is valid and becomes 3
I'd like to take a stab if there's no objection from the Go team!
For my first attempt, I simply used strconv.ParseUint (which disallows the prefixed sign), to replace the strconv.ParseInt calls (that allow it), and then cast back to int64. I'm using ParseUint(cl, 10, 63) to leave that extra sign bit, so it's safe to convert back to an int64.
From some manual testing, this seems to do the trick. I've added some unit test that reference this issue for posterity and have opened a CL.
With that change, I get
func main() {
http.ListenAndServe(":8080", http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
fmt.Println(req.ContentLength)
}))
## Before
$ go run main.go
$ echo -ne "POST / HTTP/1.1\r\nContent-Length: 3\r\nHost: 127.0.0.1\r\n\r\naaa\r\n" | nc 127.0.0.1 8080
HTTP/1.1 200 OK
...
$ echo -ne "POST / HTTP/1.1\r\nContent-Length: +3\r\nHost: 127.0.0.1\r\n\r\naaa\r\n" | nc 127.0.0.1 8080
HTTP/1.1 200 OK
...
## After
$ $GODIR/bin/go run main.go
$ echo -ne "POST / HTTP/1.1\r\nContent-Length: 3\r\nHost: 127.0.0.1\r\n\r\naaa\r\n" | nc 127.0.0.1 8080
HTTP/1.1 200 OK
...
$ echo -ne "POST / HTTP/1.1\r\nContent-Length: +3\r\nHost: 127.0.0.1\r\n\r\naaa\r\n" | nc 127.0.0.1 8080
HTTP/1.1 400 Bad Request
...
Change https://golang.org/cl/234817 mentions this issue: net/http: Disallow plus symbol in Content-Length
Thank you for the report @juliens! Thank you for working on the fix @tpaschalis, awesome!
I've added some suggestions to your CL, please take a look. Our HTTP/2 stack purposefully skips checking for errors from strconv.ParseInt, because such errors don't necessarily affect our framing. Unfortunately this increases the surface area for what could change be broken within this release that we are already in.
@bradfitz would it be pragmatic for us to firstly accept only the fix for HTTP/1.1 for Go1.15? For Go1.16 make the follow-on update in x/net/http2 and subsequently to net/http/h2_bundle.go? We ignore the errors anyways in HTTP/2 so the change could perhaps survive one more release.
Change https://golang.org/cl/236098 mentions this issue: x/net/http2: reject HTTP/2 Content-Length headers containing a sign
@odeke-em sorry for pinging you! A few days ago, I opened a similar CL for HTTP/2.
I'm not sure if I should put you as a reviewer since we had some communication for the HTTP/1.1 part, or if you're busy with other things; in any case I'd be grateful if you could take a look! Thanks in advance.
Too late for 1.15. Moving to Backlog.
HTTP/2 Content-Lengths are indeed not security-relevant, so now that this is fixed in HTTP/1.1, it's not a security issue anymore. Even in HTTP/1.1, this was only an issue when paired with a misbehaving proxy that forwards invalid Content-Length headers that it ignored, when it's supposed to strip or reject them, so we consider this a security hardening fix, and it won't be backported.
We'd like to also thank Amit Klein, Safebreach for reporting this to [email protected] before this issue was opened.
In the process of looking into this, we found another hardening fix I'll request a freeze exception for in a moment: leading zeroes are accepted the same way a leading + is. The HTTP/2 fix will probably want to fix that as well for Go 1.16.
In the process of looking into this, we found another hardening fix I'll request a freeze exception for in a moment: leading zeroes are accepted the same way a leading
+is. The HTTP/2 fix will probably want to fix that as well for Go 1.16.
Sigh, scratch that. Turns out that leading zeroes are wholly unspecified. Someone tried to make an argument for a better spec in 2008 but was met with strong resistance.
This goes to show that
(Request smuggling can still occur when two implementations both find a header valid, but disagree on its meaning, but in that case it's likely one of them is simply wrong.)
Shall we make backports for Go1.13 and Go1.14 for this issue for HTTP/1.1?
Shall we make backports for Go1.13 and Go1.14 for this issue for HTTP/1.1?
No need, we decided we'd land this as a hardening measure, not as a security fix.
Cool, thanks!
On Mon, Jun 29, 2020 at 6:16 PM Filippo Valsorda notifications@github.com
wrote:
Shall we make backports for Go1.13 and Go1.14 for this issue for HTTP/1.1?
No need, we decided we'd land this as a hardening measure, not as a
security fix.—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
https://github.com/golang/go/issues/39017#issuecomment-651458761, or
unsubscribe
https://github.com/notifications/unsubscribe-auth/ABFL3V3DH5BB4WOBWXRGAHTRZE4GXANCNFSM4M63G7DQ
.
Change https://golang.org/cl/253238 mentions this issue: src/go.mod, net/http: update bundled and latest golang.org/x/net
Change https://golang.org/cl/261919 mentions this issue: [release-branch.go1.15] src, net/http: update vendor, h2_bundle.go regeneration
Change https://golang.org/cl/258540 mentions this issue: [release-branch.go1.15] src, net/http: update vendor, regenerate h2_bundle.go
Change https://golang.org/cl/258538 mentions this issue: [release-branch.go1.14] src, net/http: update vendor, regenerate h2_bundle.go
Most helpful comment
Sigh, scratch that. Turns out that leading zeroes are wholly unspecified. Someone tried to make an argument for a better spec in 2008 but was met with strong resistance.
This goes to show that
(Request smuggling can still occur when two implementations both find a header valid, but disagree on its meaning, but in that case it's likely one of them is simply wrong.)