go: net/http: make HTTPS server detect mistaken plaintext HTTP requests and reply with HTTP 400
What version of Go are you using (go version
)?
go version go1.9 linux/amd64
Does this issue reproduce with the latest release?
Yes
What operating system and processor architecture are you using (go env
)?
GOARCH="amd64"
GOOS="linux"
Running on Ubuntu 16.04
What did you do?
https://play.golang.org/p/Vw4VokSXnYc
I migrated my project from http to https. As for https, everything is working perfectly fine (I changed the scheme, and the port remained the same). However, http still causes problems:
While https is handled fine, requesting the same address via http returns garbage and a http: TLS handshake error from [::1]:45548: tls: first record does not look like a TLS handshake
output in the application.
Of course, TLS over http does not work. The inherit problem is that it is impossible to intervene in the process. E.g. implementing a redirect from http to https (note: same port), or at very least preventing the server from returning garbage. The only place to intervene is via an http.Handler, but the handler is never called due to the tls check erroring out way before.
What did you expect to see?
If the url in the playground is requested as https://localhost:9000/hello
, The text hello
is printed out. If the url in the playground is requested as http://localhost:9000/hello
(note http), the connection is either refused (empty body and a proper status code) or redirected.
What did you see instead?
https://localhost:9000/hello
works as expected, but http://localhost:9000/hello
returns garbage (Note: Not even a proper status code is set!).
Proposed changes
I propose that the connection in above scenario should be refused properly (empty body and proper status code). Ideally, it should be possible for the user to intervene in the process, e.g. implement an https to http redirect.
About this issue
- Original URL
- State: closed
- Created 6 years ago
- Reactions: 1
- Comments: 19 (12 by maintainers)
Commits related to this issue
- net/http: fix comment change omitted between versions of CL 143177 Updates #23689 Change-Id: Icddec2fcc39802cacd651a9c94290e86cf1e48d1 Reviewed-on: https://go-review.googlesource.com/c/144517 Review... — committed to golang/go by bradfitz 6 years ago
- PrunerPing unit fails because of golang 1.12 HTTPS server change https://github.com/golang/go/issues/23689 changed how http servers in golang respond. — committed to smarterclayton/origin by smarterclayton 5 years ago
Here, instead of just returning, we could send “HTTP/1.1 400 TLS Handshake failed” and close the connection. Is there a downside to this?
To me, giving an empty reply is certainly more cleaner than replying with gibberish. But if we are to detect the protocol and respond accordingly, we might as well respond with a proper message. Just my thoughts.
Change https://golang.org/cl/143177 mentions this issue:
net/http: make server nicely reject HTTP requests to HTTPS server
For the record, this was done for the client side of net/http in #11111 by @cespare. Notably, see https://github.com/golang/go/commit/45d1c8ab59cd08ef4a7976f44c8c19ed53b118f8
The net/http server should look for that
RecordHeaderError
type.Change https://golang.org/cl/98447 mentions this issue:
net/http: make HTTPS server reject HTTP requests with HTTP 400
There is no meaningful difference between “reply with binary gibberish” and an “empty reply”. The only question is whether we go out of our way to recognize when the wrong protocol is being spoken. If we do not (because no spec says you must, and many servers don’t), then it’s completely undefined what we do.
Thanks for the comparison @bradfitz .
One other noteworthy thing is is that some of the pages (all but apache) redirect to the correct https url (in browser). I am opposed to this, but this is a subjective security point. Thus, I proposed for this to be interchangeable (e.g. make the developer decide how he wants to handle the improper request - as at the moment he can’t).
Would it be possible to detect the problem case by looking at the URL of the request maybe? If it is readable (all but the hostname is encrypted on https - right?), it should be a rouge http request. Not sure if this works tho - just some food for thought.
The essential difference from the current go implementation to the others is the fact that they do send either an empty or neat (with status code and all) response, and then reset or otherwise terminate(?) the connection. Go goes the latter, but also sends the aforementioned garbage:
It would be probably fine to remove the garbage for a first fix, and then think about how to given developers control over the matter - my 2 cents.
No, I think I understood. You’re trying to speak HTTP to an HTTPS server/port. That is undefined. You should expect browsers and any other clients (curl, telnet, whatever) to get gibberish.
Basically every other HTTP(s) server? Which ones do not? If this has become the norm lately, I’d be very surprised and intrigued.