Bug 50823 - Provide alternate failure modes for http on https
Summary: Provide alternate failure modes for http on https
Status: RESOLVED FIXED
Alias: None
Product: Apache httpd-2
Classification: Unclassified
Component: mod_ssl (show other bugs)
Version: 2.2.16
Hardware: PC Linux
: P2 trivial (vote)
Target Milestone: ---
Assignee: Apache HTTPD Bugs Mailing List
URL:
Keywords: FixedInTrunk
: 51246 55364 (view as bug list)
Depends on:
Blocks:
 
Reported: 2011-02-24 04:43 UTC by Nathan Schulte
Modified: 2013-08-07 21:51 UTC (History)
3 users (show)



Attachments
proof of concept for sending HTTP/1.0 response instead of 0.9 (929 bytes, patch)
2012-04-16 22:21 UTC, Stefan Fritsch
Details | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Nathan Schulte 2011-02-24 04:43:03 UTC
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.
Comment 1 Joe Orton 2011-02-25 12:11:39 UTC
mod_ssl: the module that can never have enough config options...

What is wrong with configuring a 400 errordoc to handle this case?
Comment 2 Nathan Schulte 2011-02-25 13:12:00 UTC
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).
Comment 3 Eric Covener 2011-09-17 17:03:13 UTC
*** Bug 51246 has been marked as a duplicate of this bug. ***
Comment 4 otheus 2012-04-16 15:00:14 UTC
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
Comment 5 otheus 2012-04-16 16:51:19 UTC
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!?
Comment 6 Stefan Fritsch 2012-04-16 22:21:22 UTC
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.
Comment 7 otheus 2012-04-18 09:56:52 UTC
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).
Comment 8 Stefan Fritsch 2012-04-18 21:15:50 UTC
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.
Comment 9 Nathan Schulte 2012-04-18 21:29:26 UTC
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?
Comment 10 Stefan Fritsch 2012-04-18 21:40:29 UTC
(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.
Comment 11 Nathan Schulte 2012-04-18 21:49:33 UTC
(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.
Comment 12 Stefan Fritsch 2012-04-20 11:26:05 UTC
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.
Comment 13 Stefan Fritsch 2012-05-05 08:45:03 UTC
fix will be in 2.4.3: r1334346
Comment 14 Nathan Schulte 2012-05-09 03:47:19 UTC
(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.
Comment 15 Eric Covener 2012-05-18 19:46:03 UTC
*** Bug 53265 has been marked as a duplicate of this bug. ***
Comment 16 Eric Covener 2012-05-18 19:47:48 UTC
(In reply to comment #15)
> *** Bug 53265 has been marked as a duplicate of this bug. ***

undone, ignore.
Comment 17 Rainer Jung 2012-08-21 16:32:12 UTC
Released with 2.4.3.
Proposed for 2.2.
Comment 18 Nick Peelman 2013-02-10 04:39:14 UTC
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
Comment 19 Rainer Jung 2013-02-15 15:56:54 UTC
2.4 patch backported to 2.2.x in r1446642.
Will be part of 2.2.24.
Comment 20 Stefan Fritsch 2013-03-03 16:42:05 UTC
2.2.24 is released
Comment 21 Stefan Fritsch 2013-08-07 21:51:15 UTC
*** Bug 55364 has been marked as a duplicate of this bug. ***