Bug 56508 - Requiring SNI - SSLStrictSNIVHostCheck semantics
Summary: Requiring SNI - SSLStrictSNIVHostCheck semantics
Status: NEW
Alias: None
Product: Apache httpd-2
Classification: Unclassified
Component: mod_ssl (show other bugs)
Version: 2.5-HEAD
Hardware: All All
: P2 normal (vote)
Target Milestone: ---
Assignee: Apache HTTPD Bugs Mailing List
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2014-05-09 08:03 UTC by Mark Nottingham
Modified: 2014-07-16 05:44 UTC (History)
0 users



Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Mark Nottingham 2014-05-09 08:03:16 UTC
I've set up my sites (https://www.mnot.net/ and https://redbot.org/) to require TLS SNI to be presented by clients; if they do not, I hard-fail.

I believe this is the most secure configuration for a site that uses SNI to serve multiple origins, since it's the only way to assure that a client doesn't get content for one origin when they think it's for another.

However, SSLStrictSNIVHostCheck is less than ideal for the task at hand.

1) When a client doesn't present SNI, the response status code is 403 Forbidden. A more appropriate status code would be 400 Bad Request (as it's similar to a missing Host header), or perhaps a new status code (happy to help there).

2) It doesn't appear possible to configure Apache to send back more than a simple string in the body of the 403 response due to missing SNI. I've configured like this:

    SSLStrictSNIVHostCheck on
    ErrorDocument 403 "TLS SNI Required."

    Listen 443
    
    <VirtualHost *:443>
        ...
        SSLStrictSNIVHostCheck on
  
      <Directory ...>
        ErrorDocument 403 default
        SSLRequireSSL
        SSLOptions +StrictRequire
      </Directory>
    </VirtualHost>

Note the re-defaulting of 403's ErrorDocument here. Configuring ErrorDocument 403 with a local file doesn't appear possible; if it is possible, the configuration is pretty convoluted (I tried a number of approaches).

Much more ops-friendly would be to have a separate SSLStrictSNIErrorDocument directive to allow people to present a page explaining to the end user that SNI is required to access this site.

3) When multiple TLS hosts are served from the same IP:Port, SNI should be required by default; not requiring it is a security risk, as explained above.
Comment 1 Jeff Trawick 2014-07-06 21:08:59 UTC
>Much more ops-friendly would be to have a separate SSLStrictSNIErrorDocument >directive to allow people to present a page explaining to the end user that SNI >is required to access this site.

Or have an optional parameter to SSLStrictSNIErrorDocument which has the same sort of string as ErrorDocument.

I think that the ErrorDocument situation is worse than you mention, in that overriding the 403 (or whatever) document to handle SNI is more widely applicable than just lack of SNI since it can't be localized to a default vhost which is used just to catch lack of SNI.  Why not?  Suppose you have a default vhost to catch this error and a user with a backlevel client tries to visit your site.  Initially the default vhost is used, then the desired vhost is selected from the Host header, then mod_ssl fails the request due to SSLStrictSNIVHostCheck.  When the 403 response is generated, the vhost used for ErrorDocument is not the default vhost but instead the desired one.  So the ErrorDocument configuration in the default vhost is not consulted.

My current hack to work around this:

Index: modules/ssl/ssl_engine_kernel.c
===================================================================
--- modules/ssl/ssl_engine_kernel.c	(revision 1608262)
+++ modules/ssl/ssl_engine_kernel.c	(working copy)
@@ -213,6 +213,11 @@
             ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server, APLOGNO(02033)
                          "No hostname was provided via SNI for a name based"
                          " virtual host");
+            ap_custom_response(r, HTTP_FORBIDDEN,
+                               "<html><head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />"
+                               "</head><body><h1>Secure communication error</h1><p>Your browser does not support "
+                               "secure communication with this web site.  Please use a modern browser such as "
+                               "Chrome or Firefox.</p></body>");
             return HTTP_FORBIDDEN;
         }
     }

When you tried

  ErrorDocument 403 /some/path

did you check where it was looking with strace or similar?  That path should be taken relative to DocumentRoot OF THE VHOST SELECTED VIA HOST HEADER.

IMO the best bet here is to not select a vhost from the host header if SNI was required and missing or mismatching, but I don't know if that is practical.

BTW, there are too many separate issues here (getting an error document defined, concern about the use of 403, concern about security).  I'd at least raise the security concern on dev@httpd after looking at past discussions related to SSLStrictSNIVHostCheck.
Comment 2 Jeff Trawick 2014-07-08 11:13:34 UTC
I had a thought on the response code issue you raised (403 vs. something else).

There's nothing at all wrong with the request from a back-level client, so 400 is not appropriate.  The request is rejected because of policy, so 403 seems appropriate.  If you have a response code in mind which you believe is more appropriate, bring it up on the dev@httpd list.
Comment 3 Mark Nottingham 2014-07-09 06:35:47 UTC
(In reply to Jeff Trawick from comment #2)
> There's nothing at all wrong with the request from a back-level client, so
> 400 is not appropriate.  The request is rejected because of policy, so 403
> seems appropriate.  If you have a response code in mind which you believe is
> more appropriate, bring it up on the dev@httpd list.

Yeah, I was looking at this again yesterday and had much the same thought. 

If I were to spec a new 4xx status code would Apache use it?
Comment 4 Kaspar Brand 2014-07-09 08:09:12 UTC
Sounds like we would end up with an overengineered solution... mod_rewrite can already do much of this:

  RewriteCond %{SSL:SSL_TLS_SNI} =""
  RewriteRule ^ /no_sni_error_page.html

(and instead of a static page, you could also handle this with a script, setting whatever HTTP status you prefer)

(In reply to Mark Nottingham from comment #0)
> it's the only way to assure that a client
> doesn't get content for one origin when they think it's for another.

Well, the client has to verify the certificate in the first place, so you can configure some kind of dummy ("snakeoil") certificate for the first vhost.
Comment 5 Jeff Trawick 2014-07-09 10:44:02 UTC
>If I were to spec a new 4xx status code would Apache use it?

IMO no need/not appropriate to have a new 4xx status code.
Comment 6 Jeff Trawick 2014-07-09 10:58:36 UTC
>RewriteCond %{SSL:SSL_TLS_SNI} =""
>RewriteRule ^ /no_sni_error_page.html

Nice trick (thanks!) though error prone...  No more "SSLStrictSNIVHostCheck On", rules have to be active in every SSL-enabled vhost (since non-SNI client will likely still get to the right vhost even though they handshaked with the default vhost), rules have to be in proper order relative to existing rules for the vhost, error page still has to be active everywhere, potentially under the document root for each vhost.  (A global Alias directive didn't handle that for me; dunno if it is the ordering between mod_alias and mod_rewrite or some error that I need to debug.)

IMO it is simply wrong to pick a vhost from the Host header if SNI clients aren't supposed to be supported (that leads to issues like needing to duplicate  directives related to no-SNI error handling in all the SSL-enabled vhosts).  But that has a migration impact in stable releases for those admins that already got this to work.

A directive like SSLStrictSNIErrorDocument + SSLStrictSNIVHostCheck=On set in global scope is a nice pragmatic solution.
Comment 7 Kaspar Brand 2014-07-12 06:23:10 UTC
(In reply to Jeff Trawick from comment #6)
> No more "SSLStrictSNIVHostCheck On",

Yes, since I always considered this to be an unnecessary/misdesigned directive (see e.g. https://mail-archives.apache.org/mod_mbox/httpd-dev/200903.mbox/%3C49D0EFF7.8030902@velox.ch%3E). The primary purpose of SNI is allowing to present the proper certificate, not enforcing access control, IMO.

> rules have to be active in every SSL-enabled vhost (since non-SNI
> client will likely still get to the right vhost even though they handshaked
> with the default vhost), rules have to be in proper order relative to
> existing rules for the vhost,

Relatively easy to achieve with "RewriteOptions InheritBefore" (or even "RewriteOptions InheritdownBefore" with 2.4.8 and later), I would say.

> IMO it is simply wrong to pick a vhost from the Host header if SNI clients
> aren't supposed to be supported

Did you mean *non-*SNI clients? I can't follow you here otherwise.
Comment 8 Jeff Trawick 2014-07-12 13:46:47 UTC
>The primary purpose of SNI is allowing to present the proper certificate, not >enforcing access control, IMO.

I think that's a perverse way of describe the desire to lock out non-SNI clients when relying on SNI to select the proper certificate.  (I mention only the certificate selection issue as I will trust for the moment all previous refutation of the security concerns about the satisfaction of all other security/access-related SSL configuration in the vhost when the client gets to the right vhost after first being handled by the wrong certificate/vhost.)

As an admin, I want clients for whom the proper certificate can't be selected to use a different browser, not to continue to use my site with a negative visual warning in the address bar.  Those users or unattended clients that do proceed after a certificate error should get a message indicating how to fix the problem, not proceed to use the site.

>Relatively easy to achieve with "RewriteOptions InheritBefore" (or even >"RewriteOptions InheritdownBefore" with 2.4.8 and later), I would say.

Thanks!

Aside from general concerns about the mod_rewrite requirements (mod_rewrite is definitely error prone in general, has unexpected behavior between configuration scopes, and many people are wise to avoid it), I still had problems with the earlier recipe and avoiding having to put the error page under DocumentRoot of every vhost.  I'll have to play with this further to sort out any errors on my part vs. real limitations.

>Did you mean *non-*SNI clients? I can't follow you here otherwise.

Yes, of course :)
Comment 9 Jeff Trawick 2014-07-12 15:36:11 UTC
>>The primary purpose of SNI is allowing to present the proper certificate, not >>enforcing access control, IMO.

>I think that's a perverse way of describe the desire to lock out non-SNI clients >when relying on SNI to select the proper certificate.

I just realized that I didn't understand the big picture from your perspective, which I think is:

Because there is no security-based reason to reject clients that don't support SNI even if you are relying on name (SNI)-based SSL virtual hosts, then having mod_ssl return FORBIDDEN (or any HTTP error) for this situation makes no sense, and any solution should be outside of mod_ssl to the extent possible.

Is that correct?
Comment 10 Kaspar Brand 2014-07-16 05:44:43 UTC
(In reply to Jeff Trawick from comment #9)
> I just realized that I didn't understand the big picture from your
> perspective, which I think is:
> 
> Because there is no security-based reason to reject clients that don't
> support SNI even if you are relying on name (SNI)-based SSL virtual hosts,
> then having mod_ssl return FORBIDDEN (or any HTTP error) for this situation
> makes no sense,

Not necessarily, but I think (only) relying on the presence of a suitable SNI extension in a ClientHello for deciding whether a client is allowed to "reach" a certain vhost is too coarse.

The assumption that configuring a more restrictive setting like SSLCipherSuite or SSLProtocol for a non-default vhost is automatically enforced when SNI is in place isn't really true, unfortunately. In ssl_callback_ServerNameIndication()/ssl_find_vhost(), we try to "fix" some things, but essentially, OpenSSL's SSL_set_SSL_CTX() implementation is too slim, IMO - see https://rt.openssl.org/Ticket/Display.html?user=guest&pass=guest&id=3183 (and https://issues.apache.org/bugzilla/show_bug.cgi?id=55707 for its implications on mod_ssl).

> and any solution should be outside of mod_ssl to the extent possible.

I think it's much more safe if SSL-related access restrictions are spelled out explicitly in the config, by means of "Require" directives (or "SSLRequire" in earlier httpd versions). E.g.:

<RequireAll>
  Require expr -n %{SSL_TLS_SNI}
  Require expr %{SSL_PROTOCOL} in { "TLSv1.1", "TLSv1.2" }
  Require expr %{SSL_CIPHER_USEKEYSIZE} ge 128
</RequireAll>

... rather than having a single "SSLStrictSNIVHostCheck" flag. Returning 403 is fine in this case, as is an appropriate ErrorDocument. But adding a per-module and per-feature error handling directive ("SSLStrictSNIErrorDocument") feels like the wrong approach to me.