Jan 14 2018

It is possible to undo a HSTS deployment... sort of

Once you start serving your web app or website over HTTPS, one really easy way to improve its security is to use HTTP Strict Transport Security, or HSTS for short. All you need to do is send a response header that looks like this:

Strict-Transport-Security: max-age=31536000

Most modern browsers will remember this header, and will always automatically upgrade every request to the same domain to HTTPS, for the number of seconds in the max-age parameter (in this case, one year).

This is great! Normally, when someone types your website into their address bar, unless they explicitly include the https:// at the start, the browser will make a plain HTTP request first. Even if the very first thing you do whenever you get a HTTP request is redirect it to HTTPS, an attacker could intercept that first redirect and make it point to a server under their control instead. With HSTS, everything goes over HTTPS from the start, so they don't get a chance.

The one caveat with deploying HSTS, or so the common advice goes, is that there's no going back. If you discover that you need to go back to HTTP, browsers that have already seen the HSTS header will keep requesting HTTPS versions of your site, and will refuse to use plain HTTP; that's the whole point. So, make sure you test everything on HTTPS, and start off with small max-age values, until you're sure everything works.

This is good advice, and you should definitely follow it, but what if it's too late? What if you discover that part of your site doesn't work with HTTPS, but you've already sent out HSTS headers with a 1-year expiry to visitors? The solution looks something like this:

HTTP/1.1 302 Found
Location: http://example.com/foo
Strict-Transport-Security: max-age=0

It turns out that you can change the max-age parameter in a Strict-Transport-Security header, and it'll override whatever the browser has stored. You can even set it to zero, which will tell the browser that HSTS no longer applies to this site. By doing that and then redirecting to HTTP, you're essentially undoing HSTS in a way that's invisible to the user.

Of course, if you totally lose the ability to serve HTTPS at all for some reason, this still won't help you. In fact, if you're stuck in this scenario there's likely no way out, because it's impossible for a browser to tell the difference between "the real server can't serve HTTPS anymore and wants me to use HTTP" and "there's a man-in-the-middle attacker that's modifying traffic and wants me to use HTTP". That said, given the abundance of certificate authorities that will sell you cheap domain-verified certificates, as well as Let's Encrypt which will give them to you for free, you're unlikely to be in this situation—even if your server won't let you set it up this way, you can put a reverse proxy like nginx in front of it and configure the redirect there.

In the more likely scenario where you've rolled out HTTPS, rolled out HSTS, then discovered that there's a part of your site that uses some third-party JavaScript or iframe that's HTTP-only and can't be replaced, you're all set. Of course, you should still serve as much as you can over HTTPS—especially things like admin panels for CMSes or anything else where users have to log in—and you should do whatever you can to get the third-party provider to support HTTPS too, but at least your site won't be broken for all your repeat visitors in the meantime.