Saturday, August 10. 2013
Service Name Indication
One or two months ago I was in Barcelona working for a customer, talking about setting several certificates in a web server he commented that Apache can setup different certificates for each virtual host on the same IP:port pair. I was surprised cos (as far as I knew) that behavior was impossible cos all the TLS stuff (handshake, certificate presentation and so on) happened before HTTP and therefore the web server does not know which host the client is trying to access at the moment of certificate presentation. But he was perfectly right, there is a TLS extension standardized by the RFC 6066 which lets the client to send the host he is trying to access in the TLS negotiation. This extension is called Service Name Indication (SNI).
Let's start with some history. For a very long time all web servers can be configured to manage different domains using the same IP:Port pair. This technique is used by web-hosting companies to give service to different domains / customers (www.customer1.com and www.customer2.com for example) using limited resources (the same web server can host a lot of customer domains). Virtual hosting in HTTP lets the browser present a Host header to mark the client is trying to access to this particular domain. For example doing a telnet connection the following GET request is trying to access to particular www.magneto.com domain or host:
$ telnet localhost 8080 Trying 127.0.0.1... Connected to localhost. Escape character is '^]'. GET /index.html HTTP/1.1 Host: www.magneto.com
This way the web server receives the Host header and uses it to display the pages that correspond to this domain. Nevertheless virtual hosts were not able to deal with secure (https) connections. Although the Host header functionality worked the same in https, the problem was that the certificate presented in the TLS connection should be different depending the domain (in my previous example if the browser was trying to access www.magneto.com the certificate for that name should be presented and not the certificate for any other site). The TLS negotiation (certificate presentation included) is done before any HTTP header is sent, so (before SNI) the web server had no clue to guess the domain the connection was going to access and therefore it always presented a fixed certificate (only one certificate could be configured for IP:Port pair). In those times typical techniques to accept secure connections for more than one site was using a wildcard certificate (*.magneto.com for example) or specifying Subject Alternative Names in it. But obviously all those techniques were workarounds and not a real solution, web-hosting companies could not configure typical http virtual hosts under https.
SNI is an extension (a field) that the browser can send in the TLS handshake which performs the same function than the Host header but in this earlier stage. That field marks that the final client is trying to access to a particular host name (that is why the extension is named Service Name Indication) and the correct server certificate can be selected. The following screenshot shows the field in a wireshark capture of the TLS negotiation. As you see the extension is sent in the client hello package.
It seems that right now any modern browser / OS supports the extension (the main exceptions are default browser in Android 2.x and any version of Internet Explorer on Windows XP). Therefore this feature is widely spread among browsers with the common exception of old IEs (previous to version 9). Talking about servers all the mayor players support this extension too in their modern versions (Apache, IIS,...). Nevertheless JavaEE containers do not support it because Java VM does not support the extension completely. Java 7 supports SNI in the client side and version 8 will support it in the server side. So I suppose that as soon as version 8 is released all Java containers will start supporting TLS virtual hosts (Tomcat, Glassfish, WebLogic, WebSphere,...).
As usual I am going to present the steps I did to test this extension in Apache 2.4:
I downloaded and installed last 2.4.6 Apache http server (for that I followed the apache compiling instructions):
$ tar jxvf httpd-2.4.6.tar.bz $ tar jxvf apr-1.4.8.tar.bz2 $ tar jxvf apr-util-1.5.2.tar.bz2 $ mv apr-1.4.8 httpd-2.4.6/srclib/apr $ mv apr-util-1.5.2 httpd-2.4.6/srclib/apr-util $ cd httpd-2.4.6 $ ./configure --prefix=/home/ricky/apps/httpd-2.4.6 --enable-ssl $ make install
I changed the listen port to 8080 and set a ServerName.
Listen 8080 ServerName magneto:8080
I uncommented the SSL module lines in the httpd.conf:
LoadModule socache_shmcb_module modules/mod_socache_shmcb.so LoadModule ssl_module modules/mod_ssl.so
And included the httpd-ssl.conf line:
Include conf/extra/httpd-ssl.conf
The self-signed certificates for two virtual hosts (www.magneto.com and www.magneto2.com) were created in the conf directory:
$ openssl req -x509 -nodes -days 365 -newkey rsa:2048 \ -subj "/C=ES/O=demo.kvm/CN=www.magneto.com" \ -keyout www.magneto.com.key -out www.magneto.com.crt $ openssl req -x509 -nodes -days 365 -newkey rsa:2048 \ -subj "/C=ES/O=demo.kvm/CN=www.magneto2.com" \ -keyout www.magneto2.com.key -out www.magneto2.com.crt
The httpd-ssl.conf were configured very very simple. Two virtual hosts were defined with the minimal attributes in the same 4443 port.
Listen 4443 SSLCipherSuite HIGH:MEDIUM:!aNULL:!MD5 SSLPassPhraseDialog builtin SSLSessionCache "shmcb:/home/ricky/apps/httpd-2.4.6/logs/ssl_scache(512000)" SSLSessionCacheTimeout 300 <VirtualHost *:4443> DocumentRoot "/home/ricky/apps/httpd-2.4.6/htdocs" ServerName www.magneto.com:4443 ServerAdmin you@example.com ErrorLog "/home/ricky/apps/httpd-2.4.6/logs/error_log" TransferLog "/home/ricky/apps/httpd-2.4.6/logs/access_log" SSLEngine on SSLCertificateFile "/home/ricky/apps/httpd-2.4.6/conf/www.magneto.com.crt" SSLCertificateKeyFile "/home/ricky/apps/httpd-2.4.6/conf/www.magneto.com.key" CustomLog "/home/ricky/apps/httpd-2.4.6/logs/ssl_request_log" \ "%t %h %{SSL_PROTOCOL}x %{SSL_CIPHER}x \"%r\" %b" </VirtualHost> <VirtualHost *:4443> DocumentRoot "/home/ricky/apps/httpd-2.4.6/htdocs" ServerName www.magneto2.com:4443 ServerAdmin you@example.com ErrorLog "/home/ricky/apps/httpd-2.4.6/logs/error_log" TransferLog "/home/ricky/apps/httpd-2.4.6/logs/access_log" SSLEngine on SSLCertificateFile "/home/ricky/apps/httpd-2.4.6/conf/www.magneto2.com.crt" SSLCertificateKeyFile "/home/ricky/apps/httpd-2.4.6/conf/www.magneto2.com.key" CustomLog "/home/ricky/apps/httpd-2.4.6/logs/ssl_request_log" \ "%t %h %{SSL_PROTOCOL}x %{SSL_CIPHER}x \"%r\" %b" </VirtualHost>
The ServerName, SSLCertificateFile and SSLCertificateKeyFile were properly configured for both virtuals.
And everything works perfectly, the SNI extension is sent by all my browsers and the correct certificate is shown depending the hostname I am trying to access. Very nice extension for web-hosting. I am very grateful to the customer for the advise, I had no idea about SNI before regardless it is a quite old feature.
You learn something new everyday!
Comments