When using HTTP over TLS from within a virtual host, the current failure mode is to attempt to return an HTML error page emulating that of a 400 status error document. I say emulating because the status code isn't in the response headers, which most browsers seem to interpret as a 200 status (when using the HTTP/0.9 protocol as the current solution does). There is also an issue in that, because the current implementation refrains from reading any more data from the socket, there is no way to retrieve any Host: header from the request. This means that the returned page shows the default canonical ServerName for any such requests, which when using virtual hosts is generally irrelevant (possibly returning a link suggesting trying to load a completely irrelevant website's HTTPS service). I realize that the latter issue can be resolved by changing the default ServerName to something that doesn't resolve, (or localhost, or similar), however, this is still suboptimal. Before I knew the details of mod_ssl, I was attempting to use mod_rewrite to redirect this request to the proper HTTPS service. This obviously didn't work. Setting the 400 ErrorDocument to use HTML <meta http-redirect ...> markup would do the trick, but that is also suboptimal, for obvious reasons. What I propose is creating a new directive that allows modification of the current behavior. The directive would enable this to be handled like any other non TLS speaking client that tries this, i.e. they get dropped. It would be nice if the current method actually set the status code, and if it was possible to disable the hint link or perhaps parse the request headers and prefer the Host: header over ap_get_server_name() for link generation. It's possible that whatever method would be implemented to allow parsing the headers would wind up kicking in the required goods to allow ap_get_server_name()'s use as it return the Host: header. I'll create a patch for the new mod_ssl directive that allows disabling of specialization of HTTP on TLS if there's any chance it would be accepted.
mod_ssl: the module that can never have enough config options... What is wrong with configuring a 400 errordoc to handle this case?
Setting an ErrorDocument for this means that all 400 requests will return this ErrorDocument. Furthermore, the ErrorDocument returned is for the default server (in virtual host terms, the first virtual host that matched). This is the same issue as with the ServerName as discussed above. This means that the ErrorDocument is then for used for all _ALL_ such accesses, regardless of the actual host that was intended (meaning no host specific links or error messages can be used in the 400 error doc).
*** Bug 51246 has been marked as a duplicate of this bug. ***
This is a pretty serious problem, because among other things, Google Bot (for instance) might pick up an erroneous link to the error page and then make it an actual search result. Since the error document does not return the header, Google and other clients are unaware this page is being reached in error. Further, there is absolutely no indications of this problem in the logfiles -- neither access nor error.log
Configuration stripped down -- only so, prefork, core, http_core, vhost and log_config are enabled. The access logs show only a request via HTTP/0.9... GET HTTP/0.9 VIRTUAL-HOST / 10.2.11.81 443 400 10.2.10.17 - Error logs show: [Mon Apr 16 18:44:47 2012] [info] [client 10.2.10.17] Connection to child 2 established (server HOST:443) [Mon Apr 16 18:44:47 2012] [info] Seeding PRNG with 144 bytes of entropy [Mon Apr 16 18:44:47 2012] [debug] ssl_engine_kernel.c(1761): OpenSSL: Handshake: start [Mon Apr 16 18:44:47 2012] [debug] ssl_engine_kernel.c(1769): OpenSSL: Loop: before/accept initialization [Mon Apr 16 18:44:47 2012] [debug] ssl_engine_io.c(1795): OpenSSL: read 11/11 bytes from BIO#2b5e9dd6c6f0 [mem: 2b5e9dd73dc0] (BIO dump follows) [Mon Apr 16 18:44:47 2012] [debug] ssl_engine_io.c(1742): +-------------------------------------------------------------------------+ [Mon Apr 16 18:44:47 2012] [debug] ssl_engine_io.c(1767): | 0000: 47 45 54 20 2f 20 48 54-54 50 2f GET / HTTP/ | [Mon Apr 16 18:44:47 2012] [debug] ssl_engine_io.c(1773): +-------------------------------------------------------------------------+ [Mon Apr 16 18:44:47 2012] [debug] ssl_engine_kernel.c(1798): OpenSSL: Exit: error in SSLv2/v3 read client hello A [Mon Apr 16 18:44:47 2012] [info] [client 10.2.10.17] SSL handshake failed: HTTP spoken on HTTPS port; trying to send HTML error page [Mon Apr 16 18:44:47 2012] [info] SSL Library Error: 336027804 error:1407609C:SSL routines:SSL23_GET_CLIENT_HELLO:http request speaking HTTP to HTTPS port!?
Created attachment 28618 [details] proof of concept for sending HTTP/1.0 response instead of 0.9 This fakes the end-of-headers CR/LF pair so that the core won't get confused when trying to read the HTTP/1.0 headers. This seems to work, but reviewers would be welcome.
Thanks, Stefan. Perhaps I'm confused, but all this patch does is add a CRLF so that an inline ErrorDocument 400 "HTTP/1.1 400 You requested a non-SSL resource from an SSL service" will actually be usable by conforming clients. Further, the line: "GET / HTTP/1.0" CRLF muddies the waters. Perhaps I don't understand the context that line is used in, but it seems to me that matches an input string from the client. I dont think that's at issue here. To clarify: From a standards standpoint, I think it's absolutely incorrect to allow a port configured for SSL (SSLEngine = "on") to operate in any other way. An HTTP request should simply be closed, period. (If SSLEngine = "optional", then the server should act like a normal HTTP/1.1 server until the upgrade handshake is initiated.) Excepting that, the response should be one of 400 or 426. Maybe this should be user-configurable, but I think it matters not. Even a hardcoded response of 400 or 426 is better than what is there now. One possibly desirable behavior would be a redirect (301).
I admit that my comment was not very helpful for a non-developer. What happens if an http request arrives on an https port is that openssl consumes part of the request, recognizes that it was http and not ssl, and returns an error code to httpd. Since the full request is not available any more, httpd then injects a fake HTTP 0.9 request so that it can use its usual request machinery to send an error response. What my patch does is change the fake HTTP request from HTTP 0.9 to 1.0. This requires adding the protocol version and the CR/LF that separates the headers from the body. Due to the internal workings, these two parts have to be inserted at different points. The result is that you will get a HTTP/1.0 response with a proper 400 bad request status line. As I understand it, this should already help a lot with search engines indexing that page. About the error message itself, I would say that if we can't be sure that the link we send is correct, we should rather send no link at all. As mentioned above, parsing the request for the Host header is not that easy at that point because openssl has already eaten part of it.
New life to an old bug; awesome. I still like my original proposal of a directive allowing HTTP on TLS / SSL / HTTPS to be handled as per the specification: it gets dropped. Shortly after opening the report I started grokking the src to attempt a patch, but I apparently never finished. I've checked out the src and will start again here shortly. I will also attempt to verify the HTTP 1.0 / 400 status patch. I effecting that patch, as well as removing the mischievous link would help immensely. Is returning a document with a script to sed -e 's/http:\/\//https:\/\//' window.location or document.URL and place it as the href of an anchor out of the question?
(In reply to comment #9) > I still like my original proposal of a directive allowing HTTP on TLS / SSL / > HTTPS to be handled as per the specification: it gets dropped. I am not sure that we need yet another directive if the default behavior is OK. But at least since we support SNI, the current behavior is just wrong. > I will also attempt to verify the HTTP 1.0 / 400 status patch. I effecting > that patch, as well as removing the mischievous link would help immensely. Is > returning a document with a script to sed -e 's/http:\/\//https:\/\//' > window.location or document.URL and place it as the href of an anchor out of > the question? That sounds like a good idea.
(In reply to comment #10) > I am not sure that we need yet another directive if the default behavior is OK. > But at least since we support SNI, the current behavior is just wrong. I agree; a new Directive isn't necessary so long as the default behavior is suitable, and the current default is not. > > I will also attempt to verify the HTTP 1.0 / 400 status patch. I effecting > > that patch, as well as removing the mischievous link would help immensely. Is > > returning a document with a script to sed -e 's/http:\/\//https:\/\//' > > window.location or document.URL and place it as the href of an anchor out of > > the question? > > That sounds like a good idea. This kind of dances around the issue, as with supporting SNI the server should/would/could be expected to already know this. I don't see anything wrong with it though, but I'm not sure how the Project views it. If having a script in the error page is allowable and if a redirect is deemed a better solution than suggesting the user might have meant HTTPS rather than HTTP, that could be done via script as well. I don't think a mandatory redirect assuming the client meant HTTPS rather than HTTP is correct, though.
Committed a slightly different version to trunk: r1328325 Removed the link completely for now: r1328326 But if someone provides the javascript that adds the https link, I would be happy to add that.
fix will be in 2.4.3: r1334346
(In reply to comment #12) > But if someone provides the javascript that adds the https link, I would be > happy to add that. Here is a (i.e. one of many possibly suitable version) JavaScript function that will append the removed link, block quote and all. function fix() { var p = document.getElementsByTagName('p')[0]; var href = window.location.toString().replace(/^.*:\/\//i, 'https://'); var bq = document.createElement('bq'); bq.innerHTML = '<a href="' + href + '"><b>' + href + '</b></a>'; p.parentNode.insertBefore(bq, p.nextSibling); } Just in case, it functions as follows: 1) Find the first paragraph (p) element in the document. This should be the one added by the block of code modified to remove the link, but as I'm unfamiliar with Apache, I don't know if it's possible for some user configuration or other code to add another prior. 2) Case-insensitively Replace the protocol, even if it's not "http", with "https". 3) Create the blockquote element and stuff the link as it's contents, finally adding the blockquote element and it's children to the document immediately after the paragraph element. To test, I added it inside a script element (<script language="JavaScript"></script>) in the HTML head element of the saved error page, and added 'onload="fix();"' to the body element. This requires modification of the head and body elements which may or may not be an option. I'll whip up another if my assumptions aren't suitable, just let me know my constraints.
*** Bug 53265 has been marked as a duplicate of this bug. ***
(In reply to comment #15) > *** Bug 53265 has been marked as a duplicate of this bug. *** undone, ignore.
Released with 2.4.3. Proposed for 2.2.
After having been directed here via an answer to my ServerFault question (link below), I wanted to throw my hat in the ring that you should *at least* be able to configure this to automatically redirect. You can handle standard Port 80/443 redirects quite easily via mod_rewrite, but if you're hosting multiple sites via various ports because you're limited on IP space, it is quite confusing and annoying to end users (and the meticulous server admin who has to deal with them) if/when you when they forget an "s" or are sent a link that forgot the "s", and they have to deal with a very dumb (in both content and purpose) error page. Granted that's a much nicer behavior than simply dropping the traffic. I can *almost* understand the behavior for 80/443 connections. Almost. But for outside of those ports, having the option to turn on auto-redirect which would fire a 301 Permanent Redirect would be fantastic. I'm told via the thread (link below) that 2.4 exhibits better behavior here, but it still doesn't seem ideal. Googling around for solutions to this particular predicament, I'm far from the only person using Apache and SSL on alternate ports, and there are quite a few people who were also struggling with this very problem. Ideally I suppose you'd have three (or four) options for this behavior: 1) (Default) show an error message, like it does now, except do it properly, send the right headers, handle it like a tradition ErrorDocument-calling-status, etc. 2a) 301 redirect the traffic to https. If there are concerns about the client handling the traffic if its expecting http, then tell me why the current solution (or just dropping the traffic altogether) is a better solution. 2b) Allow 301 redirection to a custom page (could implicitly encompass 2a) 3) Drop the traffic ServerFault Thread: http://serverfault.com/questions/477236/apache-insecure-request-sent-to-secure-port-want-to-redirect/477265#comment529069_477265
2.4 patch backported to 2.2.x in r1446642. Will be part of 2.2.24.
2.2.24 is released
*** Bug 55364 has been marked as a duplicate of this bug. ***