There are two basic ways to make this work1. The first is to give the machine two IP addresses. You then have one virtual server listen on one IP and the other on the other (the exact implementation details don't matter). This is called IP-Based Virtual Hosting (IPBVH) and is the old-style way of doing things. The problem, of course, is that IP addresses are a scarce resource and so it's antisocial (and also kind of expensive) to grab enough IP addresses for every virtual host.
The modern way to do things is with Name-Based Virtual Hosting (NBVH). The way that this works is that the Web client provides the server's hostname in its HTTP request, like so:
GET / HTTP/1.1 Host: www.educatedguesswork.org:8080 User-Agent: Mozilla/5.0 (X11; U; FreeBSD i386; en-US; rv:1.7.5) Gecko/20041118 Firefox/1.0 Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5 Accept-Language: en-us,en;q=0.5 Accept-Encoding: gzip,deflate Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7 Keep-Alive: 300 Connection: keep-alive
Name-Based Virtual Hosting has two problems. The first is that it requires cooperation from the browser. That's not a big deal now because all browsers have done it for years. The second, larger, problem is that it doesn't work with SSL. The problem is that the SSL handshake happens before the first HTTP request is made, so the server doesn't know which certificate to use in its handshake. Because the certificate contains the host name (otherwise how would you know if you were talking to the right host?) this means that the hostname and cert generally won't match, which tends to freak users out. There is an update to SSL that puts the hostname in the SSL handshake (client cooperation again, naturally), but since it requires changes to the client it will be years before there's enough deployment to let you confidently use NBVH with SSL for most commercial applications.
There is, however, one situation in which NBVH with SSL works just fine--if you don't need the certificate to match the host. The most common situation where this applies is when you're using self-signed certificates rather than third-party certificates. In this environment, you either don't worry about active attack and don't have the users verify the certificate or have them verify the cert fingerprint manually and then trust it from there on (as in SSH). Even if you don't verify the certs at all, it's still better than not having SSL, and there's nothing wrong with the fingerprint method except that it's inconvenient.
In this mode, the server uses the same certificate for all requests, but then does virtual hosting for the HTTP portion. This works fine technically but it's a little hard to set up and the Apache documentation doesn't help much. However, Rob Austein and I got it working and here's an example of the relevant section of the httpd.conf file, with the comments removed:
AddType application/x-x509-ca-cert .crt AddType application/x-pkcs7-crl .crl NameVirtualHost 127.0.0.1:8443SSLPassPhraseDialog builtin SSLSessionCache dbm:/users/ekr/tmp/apache/logs/ssl_scache SSLSessionCacheTimeout 300 SSLMutex file:/users/ekr/tmp/apache/logs/ssl_mutex SSLRandomSeed startup builtin SSLRandomSeed connect builtin SSLLog /users/ekr/tmp/apache/logs/ssl_engine_log SSLLogLevel info SSLCipherSuite ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP:+eNULL SSLCertificateFile /users/ekr/tmp/apache/conf/ssl.crt/server.crt SSLCertificateKeyFile /users/ekr/tmp/apache/conf/ssl.key/server.key ## ## SSL Virtual Host Context ## # General setup for the virtual host DocumentRoot "/users/ekr/tmp/apache/htdocs/lh1" SSLEngine on # General setup for the virtual host DocumentRoot "/users/ekr/tmp/apache/htdocs/lh2" SSLEngine on
There are two keys to making this work:
- The NameVirtualHost directive which lets you use name-based virtual hosts at all.
- Putting SSLEngineOn in the virtual host directives.
Acknowledgements: Rob Austein and I worked on this together. I figured out how to make it work but I duplicated all of the SSL config directives in each VirtualHost clause. Rob figured out that you could move them to the main IfModule section, thus cleaning things up substantially.
1. There's actually a third way to make this work: use a separate port for each host. The problem with that is that it requires users to remember the port as well as the hostname, which is pretty unattractive.
Most current browsers seem to support regular expressions (and not just wildcards) in the Common Name field. I wouldn't use it personally, but I've seen a lot of these certificates from browser CAs (at least I think so, I don't use these CAs and store all certificates manually).
By the way, IPv4 addresses for obscure HTTPS servers quite cheap because renumbering is usually not an issue (you just do it), and PA address space isn't exactly expensive for end users.
Is it possible to have the certificate bind the IP address rather than the host name? Would that satisfy browsers?
Regexps work a lot better if the domain names are related. Unfortunately, if you're running a hosting service, they're not. As for the availability of IPv4 addresses... They're not incredibly expensive but they're not free either. I'm paying like $5/month for the one the runs https://www.educatedguesswork.org/
Hal:
Putting the IP address in the cert doesn't protect you against active attack due to the possibility of DNS spoofing.
It would only take a few software packages adopting the TLS extensions for them to become commonplace:
IE, Mozilla, OpenSSL, MS Secchannel, IIS, Apache.
Everyone else will fall in line post haste.
Unfortunately, I think this assessment is unduly optimistic. Certainly IE and Mozilla could deploy the extensions tomorrow but it takes the installed base quite some time to catch up.
I've used the third approach in a few situations - it actually works pretty well with conventional named-based virtual hosting since you can simply have the HTTP server redirect to the appropriate HTTPS service on a non-standard port. Since users almost never type https URLs in by hand this is surprisingly painless.