Detecting SNI with Apache

From eBower Wiki
Jump to: navigation, search

What is SNI?[edit]

SNI stands for Server Name Indication, and here's the layman's version of how it works. Prior to SNI, when your browser connected to a site via an encrypted connection the first thing it does is say "give me your cert." The certificate has a hostname associated with it, if that hostname didn't match what you typed into the browser the browser would throw up a big warning message. This works great when you've got a single hostname on your server, but what happens if you have Apache virtual servers where a single IP address is responsible for serving content to multiple servers? Now Apache doesn't know which cert to send out because the browser isn't asking for a cert for a specific server, it's just asking for a default cert.

There are some workarounds for this, you can create a cert with multiple hostnames called a SAN cert or even one that supports arbitary hosts under a domain called a Wildcard cert. This usually needs to be a paid cert as opposed to one of the free certs you can get anyplace, but if you've got something complicated enough to need multiple hosts chances are you can afford a few bucks a year for a cert. It does work well for these sorts of applications, so you can have www, login, images, and all on the same cert. Or even * so you can put an arbitary number of redundant or performance-based datacenters out there. The problem is this doesn't work so well for hosting providers (or cheapskates). A common hosting provider would need a SAN cert that reveals every customer on their server, this is a privacy and security issue. A wildcard cert works well, but only if customers are willing to use hostnames like - pretty ugly.

Enter SNI. With SNI your browser no longer says "gimme your cert" but instead more politely asks "do you have a certificate for" Now Apache can look up in its host list to see if it has a cert for and send it out. SNI has a minor security issue (you can imagine a serious breakage in the SSL protocol could allow a website to generate a cert for any arbitrary hostname on the fly - but if this happens we're pretty much screwed anyway), but it has a huge amount of benefit. Every browser known to man supports it. We'll, every browser not running on Windows XP. And there's the rub, if you've got a single user running XP they'll get a big warning that it's not going to work, a warning many users will never click through to see instructions on how to proceed. XP can be fixed, firstly by installing a more modern OS that will continue to receive security updates post 2014 but more trivially by installing Firefox. Chrome will also work if you install the latest OpenSSL libraries, but IE is just broken on XP. Some embedded browsers may also have issues, so it couldn't hurt to put a check in.

The Theory[edit]

First we need a page that doesn't need SNI. This could be a non-SSL site, or it could be an SSL site that is configured as the default cert for Apache. Once we've got a page we know browsers will be OK with, we can try to grab an object from a site that needs SNI to function. If it works, we're golden. If it doesn't we can do whatever we want, I would recommend logging the user agent string and forwarding the user to a page describing what they need to do to gain access (include suggestions for upgrading their OS/browser and instructions to bypass the upcoming cert warning).

Configuring Apache[edit]

First, I'm assuming you've got some level of knowledge around configuring Apache for SSL, otherwise you probably don't need this. Here are some additions you need for a SNI-enabled site. In the /etc/apache2/sites-available/default-ssl file (or whatever you call it) you'll want to add this to the top of the file:

<IfModule mod_ssl.c>
SSLStrictSNIVHostCheck off

You'll want to set up a default cert like this:

<VirtualHost *:443>
        SSLCertificateFile    /etc/apache2/ssl/default.crt
        SSLCertificateKeyFile /etc/apache2/ssl/default.key

You just need to replicate this for your virtual host:

        SSLCertificateFile    /etc/apache2/ssl/sni-enabled.crt
        SSLCertificateKeyFile /etc/apache2/ssl/sni-enabled.key

You should now be able to go to and to without having any issues on a modern browser.

Configuring the Webpage[edit]

Now here's the easy part, the Javascript to check if SNI is working at the client. Again, I'll assume you've got a passing familiarity with JavaScript:

function check_sni() {
  var img=document.createElement('img');
  img.onload = function() {
    // Execute this block if SNI works.
  img.onerror = function() {
    // Execute this block if SNI doesn't work.