Many web developers know about SSL, but it is very common to see it only partially deployed, or not deployed where it should be. This basic guide on when and how to deploy SSL will help you avoid the most common mistakes.
- If you have any kind of confidential information, or if you have logins, even if they are just for admins, you should deploy HTTPS. The risks are not theoretical.
- Never deploy HTTPS partially: use it for all content, or many risks are left open, like the interception of session IDs, which is almost as good as passwords.
- When you deploy HTTPS, enforce all requests to be served over HTTPS, by redirecting any plain HTTP requests to HTTPS URLs.
- Enable strict transport security (HSTS) to further reduce the opportunity for attacks.
- Set the secure flag on your cookies, like the session cookie, to make sure they don’t leak out through plain HTTP requests.
- If you are running OpenSSL 1.0.1, make sure you use OpenSSL 1.0.1g or newer, or a version which has been otherwise patched for heartbleed. In general, keep your OpenSSL up to date.
- Make sure to configure your ciphers properly as well. Preferably, you should support forward secrecy. Many default configurations are insufficient. Check this with the Qualys Labs SSL server test.
Last updated: July 5, 2014: added section on ciphers.
What is HTTPS?
HTTPS refers to the layering HTTP on top of SSL/TLS. The end result is that the HTTP traffic, including requested URLs, result pages, cookies, media and anything else sent over HTTP, is encrypted. Someone interfering with the connection can neither listen in on traffic, nor change it. In addition to simply encrypting, the identity of the remote server is verified: after all, having an encrypted connection is a lot less useful if you don’t know who’s at the other end. The end result is that it becomes incredibly difficult to intercept the traffic. It might still be possible to know which websites a user is visiting, but no more than that.
When and why should I deploy HTTPS?
You should deploy HTTPS whenever your website has any kind of non-public information. That includes any website that has logins – after all, if it were public information, it would not need a login. It also includes logins only used by administrators, like in your typical Wordpress website.
You should deploy HTTPS because without it, even someone doing passive eavesdropping, i.e. just listening to the network traffic and not manipulating it, can read along with all HTTP traffic, including any passwords or authentication tokens.
This is not a theoretical attack. I have done this myself (with permission) several times – this is particularly easy on public hotspots. Public hotspots typically apply no wifi encryption, which makes it trivial to eavesdrop on all traffic. This is a very common setup in bars, hotels, trains, and other public places. In other words, if your users sometimes use your website from a public hotspot, and you do not use HTTPS, anyone in the vicinity can listen in on all their traffic. This isn’t the only case where eavesdropping might happen, but it is a very easy one.
What if I just use HTTPS for my login page?
No. Using HTTPS just for the login page will prevent your user’s passwords from being eavesdropped, but this is only part of the problem.
First of all, the less HTTPS on your website, the easier it becomes to do active interception: your login link might point to an HTTPS URL, but if I change that link before the user clicks on it, it will not help you. But, using HTTPS partially also leaves risks open for passive interception.
Verifying a username and password is only one part of authenticating users on the web: we also need to remember that a particular user was authenticated, and which account they authenticated with. The most common method is session cookies. Typically, this means the browser stores a long random string, the session ID, in a cookie. PHP for example, uses the
PHPSESSID cookie for this. A database on the server side then knows that that random string belongs to a particular session, in which a particular user authenticated himself. If I somehow acquire the session ID of your session, after you login, I acquire all permissions you have: almost as good as having your password.
Knowing this risk, the session ID is very long and random, and has a limited lifetime, meaning I can’t just guess it: this is what makes it safe enough. But, due to the way cookies work, the browser includes the cookie in every request it makes to your website. So even long after login, every page I request, even if it is usually public, will result in my session cookie being sent by the browser. And if someone is eavesdropping at that point, they can still compromise my account.
The same can happen when you only place the administrator part of your website behind SSL: when you log in and later visit the non-SSL public part, the browser will still be sending the session cookie.
In short: as session cookies, which allow access to the user’s account, are sent in every request, simply securing the login page is absolutely insufficient.
How do I enable HTTPS properly?
Enforce HTTPS usage
Some websites buy an SSL certificate, configure it on their web server, and assume they’re done. But that just means you enabled the option of HTTPS – which users are unlikely to notice. To make sure everyone benefits from your HTTPS support, you should redirect all requests that come in on HTTP, to HTTPS. That means any user visiting your site will automatically be switched over to HTTPS, and from that point on their traffic is secure.
This still leaves a small gap: the first time the user makes a request to your website, they will use plain HTTP, and they may already transmit confidential information at that time. It also leaves a small man-in-the-middle hole open.
Strict transport security
For further tightening, enable HTTP strict transport security (HSTS). This is a special header that can be sent by the server, which indicates: for a defined time period, you must not access this website over plain HTTP, or access it over HTTPS when it has a dodgy certificate. Optionally, subdomains can be included as well. HSTS makes SSL stripping very hard.
It’s a simple server header, and trivial to configure. Note though that there is no way to revert the setting before the
max-age has expired, so think carefully before you enable it. You use HSTS next to an HTTPS redirect, not in place of it.
Cookies, including the session cookie, have an optional flag called
secure. This basically means: “never send this cookie over a plain HTTP connection”. Enable this flag on your cookies, and they will not be sent with the HTTP request the browser does initially – but only once the connection switched to HTTPS, and can no longer be eavesdropped.
If you are running OpenSSL 1.0.1, make sure you are running OpenSSL 1.0.1g or newer, or that your vendor’s OpenSSL has been patched for the heartbleed bug. The heartbleed bug is an incredibly serious bug, and if you deploy HTTPS with a vulnerable OpenSSL, you are actually dramatically decreasing the security. Verify this before you enable SSL, as once you have SSL enabled with this vulnerability, all data that passes through the webserver is at grave risk.
There are other important vulnerabilities, like the OpenSSL CCS injection vulnerability (CVE-2014-0224), so you should continuously ensure your OpenSSL is kept up to date.
There are many unsafe historical ciphers for SSL, which you should disable. Default configurations are often not good enough. With the proper ciphers, you can also offer forward secrecy, which means that if your private SSL key is compromised at a later time, traffic intercepted in the past can not be decrypted.
Configuring your ciphers with forward secrecy and compatibility for older browsers is not trivial. Fortunately, the Qualys Labs SSL server test does an excellent job at determining whether you have unsafe ciphers enabled, and which cipher several major browsers will choose. My personal configuration at this time is:
ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128: DH+AES:ECDH+3DES:DH+3DES:RSA+AESGCM:RSA+AES:RSA+3DES: !aNULL:!MD5:!DSS`
However, on older systems some ciphers may not be available, so don’t copy this without testing.
Can I just deploy SSL for authenticated users?
No. Once you’ve followed the guidelines above, at the moment a user makes a plain HTTP connection, you do not know whether they are authenticated. That’s the whole point: they should not transmit any secret information, like their session cookie, until they are on SSL.
Although I can imagine some ways to work around this, they might break at some point. As the cost of SSL is really quite low nowadays, it’s not worth it.
From this blog, you may also like Why your certificate authority rarely matters, and expensive certificates are not safer, But where is the decryption key? and Proof of concept: arbitrary remote code execution through pickle-backed cookie-based sessions
For more details on how to configure your ciphers and other SSL settings correctly, have a look at the Qualys Labs SSL server test, their best practices document, GitHub’s considerations when improving their SSL setup and the Mozilla TLS documentation, although that last one can be a bit daunting for the inexperienced. Applied Crypto Hardening is also very promising, but still in draft, offering both directly usable config snippets, and extensive explanation of rationale behind the choices.