I figured it would be easy add HTTP Strict-Transport-Security to a web application, so I gave it a try. It was easy to add it. But not that easy to get it to work.
The purpose of Strict-Transport-Security is for a web site to tell browsers to only connect to the site via a secure connection, even if the user just enters a normal http:// URL. The site I was working on already redirected all http requests to a secure connection, so what’s the point of this here? In this case, just to avoid a potential (but for this site, unlikely) man-in-the-middle attack when the user first connects. That non-secure initial request could be intercepted and the user redirected to some other site that looks right, but is an imposter. With Strict-Transport-Security, the browser will never even make the initial non-secure request, avoiding this possibility.
Adding this to a web site is trivial: just return a special header with the secure web pages. For example, I returned:
Once the browser sees this header from any secure page on your website, it is supposed to remember (for the next 7,776,000 seconds, or 90 days) to never try to connect to the site other than by a secure connection. It’s also supposed to prevent the user from overriding any SSL certificate warnings, so if somebody does spoof your website with a bad certificate it won’t even give the user a chance to override the warnings and connect to the site.
Only it didn’t do that. It didn’t do anything at all that I could tell. I had a self-signed certificate, and the browser let me override the warning. I had a certificate with a name not matching the URL, and the browser let me override the warning. I tried to connect via http instead of https, and the browser went ahead and did it. (By the way, by “browser” I mean Chrome, but Firefox and Opera behaved the same way.)
It turns out that the browser will only obey this header if it is sent from a secure web page (as clearly stated in the documentation) that has no certificate warnings or errors (something I didn’t realize). Once I set up my test site to appear to be at the production URL, the Strict-Transport-Security header started working as expected.
I wasn’t expecting it to work this way, but it turns out to be really useful behavior. Now I can deploy test and development sites with wrong certificates (self-signed, or for the production instead of test URL) and not have Strict-Transport-Security lock me out. It only activates the lock in the first place if you’ve already shown you can open it, by first connecting to the secure site with no certificate problems. Now I just have to be careful to check that it does work when put into production.