-
Notifications
You must be signed in to change notification settings - Fork 17.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
net/http: make HTTPS server detect mistaken plaintext HTTP requests and reply with HTTP 400 #23689
Comments
This isn't a Go question, really. You need to listen on two separate ports: one for HTTP and one for HTTPS. You could in theory sniff the first few bytes of the connection and guess the protocol, but nobody does that because everybody uses 80 and 443, as those are the protocol defaults if no explicit port number is provided. |
@bradfitz I think you misunderstood the question. I don't want to implement a standard redirect e.g. from http:80 to https:443. Please read the What did you expect to see? and What did you see instead? sections again: The problem is, that go responds with garbage, when using a TLS server and making an http (not: NOT https) call. I'm not aware of any server who behaves like this. Or in other terms - go shouldn't even respond at all, when doing an http call against a TLS server. The problem is hidden by the fact that http and https have different standard ports (referring to the redirect on two ports). But the samples in the issue description detail how to get the mentioned garbage output. |
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. |
Okay, so Apache has a nice error message for this nowadays:
But Microsoft's webserver doesn't:
And Google doesn't:
Cloudflare (nginx?) does reply nicely too:
So, that's interesting. Apparently Apache and nginx do. I wonder what Node's built-in server does. I'll consider this a feature request and reopen but it's not a priority for me. |
/cc @tombergan as FYI |
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. |
This -
So it seems this is the same approach as Google's servers. I like this empty reply approach. Should we implement this ? |
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. |
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. |
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? |
@oliwer, there's no guarantee at that line that Handshake hasn't already written to the socket. |
@bradfitz you're right! My bad. I'm looking at how to inspect the request before or during the handshake but it is quite invasive. |
Change https://golang.org/cl/98447 mentions this issue: |
@bradfitz is any documentation update required for this? |
No. |
Change https://golang.org/cl/143177 mentions this issue: |
Change https://golang.org/cl/144517 mentions this issue: |
Updates #23689 Change-Id: Icddec2fcc39802cacd651a9c94290e86cf1e48d1 Reviewed-on: https://go-review.googlesource.com/c/144517 Reviewed-by: Ian Lance Taylor <iant@golang.org>
golang/go#23689 changed how http servers in golang respond.
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
)?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 texthello
is printed out. If the url in the playground is requested ashttp://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, buthttp://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.
The text was updated successfully, but these errors were encountered: