Getting HTTPS on Amazon CDN (without paying for custom domain SSL)
Recently I had to deal with the following problem: supporting HTTPS on a web page stored on Amazon S3, delivered with CloudFront.
Why is this so complex?
Let’s make a step back: SSL certificates basics, from a handshaking point of view. I won’t talk about data encryption here.
What Happens When a Browser Encounters SSL
- A browser attempts to connect to a website secured with SSL.
- The browser requests that the web server identify itself.
- The server sends the browser a copy of its SSL Certificate.
- The browser checks whether it trusts the SSL Certificate. In order to do this, browsers include a set of trusted Certificate Authorities (CA) certificates, used to verify the received certificate signature. If so, it sends a message to the server. The certificate received must match whatever domain the user requested.
- The server sends back a digitally signed acknowledgement to start an SSL encrypted session.
- Encrypted data is shared between the browser and the server and https appears.
An SSL certificate is tied to a domain (or a set of domains, using wildcards); also you cannot have more then one certificate configured per IP address. Since SSL handshake takes action before any HTTP happens, the server cannot send a specific certificate for different domains.
On a single-host server, adding a SSL certificate is a trivial task, involving a certificate generation and a simple web server configuration.
In a cloud network, on the other hand, every endpoint serves different content from several customers. As SSL is designed, is not possible to
This problem is solved with the introduction of Server Name Indication (SNI), an extension to the TLS protocol that is supported by most modern browsers, which allows multiple domains to serve SSL traffic over the same IP address.
Why is SSL on cloud so expensive?
Amazon offers the possibility to serve HTTPS requests through your domain using SSL, but this requires a dedicated IP for your domain on every endpoint in the cloud. The bigger the cloud, the more expensive this will be. Amazon, at the moment, offers this options at the price of 600$/month per domain.
If you don’t need your domain in the base request – in other words, in the browser’s address bar -, you can still use the base certificate offered by CloudFront, which uses the wildcard *.cloudfront.net. You’ll see something like https://dabc123def543.cloudfront.net, along with a valid SSL certificate.
In alternative, Amazon CloudFront can be configured to serve HTTPS requests using SNI without additional charge. It’s easy to understand that this is not a cost for them: there’s no need to get a number of dedicated IPs, but it’s just a simple configuration that is propagated to every endpoint.
So, why SSL is still needed?
Some older browsers don’t support SNI: if you need those browsers to use HTTPS, you still need to provide SSL instead of SNI.
Our solution
A proxy html page has been created with the following javascript code:
(function check_sni() {
var img=document.createElement('img');
img.src='https://my.cloudfront.domain.com/sni-test-pixel.png';
img.onload = function() {
// Execute this block if SNI works.
location.href = "https://my.cloudfront.domain.com/index.html";
}
img.onerror = function() {
// Execute this block if SNI doesn't work.
location.href = "https://d.cloudfront.net/index.html";
}
img.style.display='none';
document.body.appendChild(img);
})();
This is a self executing function that tries to load a 1-pixel image using HTTPS from my.cloudfront.domain.com. This is a CloudFront domain with only SNI enabled.
If browser supports SNI, the image is correctly loaded and the onload event fired. A redirect to the CloudFront SNI enabled domain is performed, anche the user correctly sees that his connection is safely encrypted.
If browser doesn’t support SNI, the onerror event is fired. A redirect to the CloudFront random-genernated third level domain is performed. The users won’t have your domain name on the address bar, but at least the connection will be encrypted.
Sources & further readings:
https://www.ebower.com/wiki/Detecting_SNI_with_Apache
http://www.symantec.com/connect/blogs/how-does-ssl-work-what-ssl-handshake