There is a common pitfall when implementing HTTP Strict Transport Security on sites that 301 redirect from x.com -> http://www.x.com which leaves your users open to a MITM attack. Paypal is an example of one of the sites affected by this issue. There are some solutions but none are ideal.
HTTP Strict Transport Security (HSTS) is a mechanism for websites to instruct browsers to only ever connect to them using an encrypted HTTPS connection. It solves the problem of man in the middle attacks (MITM) (except on the browser’s first ever connection to the site) which could allow an attacker to keep the user from connecting the secure version of the site and do evil things to them.
It’s generally very simple to setup. You just 301 redirect any HTTP connections to the HTTPS version of your site and then you add an HTTP header (Strict-Transport-Security: max-age=TTL_IN_SECONDS) to every HTTPS response. That’s it. Note that you cannot put the HSTS header on unencrypted HTTP responses because this would allow an attacker to expire your HSTS policy prematurely.
HSTS with Canonical Redirects
Because of that last point it gets a little trickier when dealing with a website that does canonical redirects. A canonical redirect is just a permanent 301 redirect. For example, yoursite.com 301 -> http://www.yoursite.com or http://www.yoursite.com 301 -> yoursite.com. This is fairly standard these days for SEO purposes (go to paypal.com for an example).
Let’s say your canonical redirect is from yoursite.com to http://www.yoursite.com. So you setup the http://yoursite.com -> https://www.yoursite.com and http://www.yoursite.com -> https://www.yoursite.com 301 redirects and you set the HSTS header on https://www.yoursite.com responses. This protects any user who goes directly to http://www.yoursite.com in their browser. But most often people will type in the domain without the www. Those users will still be open to MITM attacks as the http://yoursite.com -> https://www.yoursite.com redirect will always be unencrypted. The HSTS headers on http://www.yoursite.com will not affect yoursite.com.
For a real world example of this let’s check out Paypal. They were the “first major internet site” to start using HSTS. To be fair they are still beta testing it (with a max-age of less than 10 minutes) and mention explicitly that it’s only enabled on http://www.paypal.com. Although they don’t mention the ramifications of that.
Using the latest version of Google Chrome (which is the only non beta browser that currently supports HSTS) go to paypal.com. Then open Developer Tools and go to the Resources tab. You’ll see an unencrypted 301 redirect from http://paypal.com to https://www.paypal.com. The response from https://www.paypal.com will set the HSTS policy. Now open a new tab and go to http://www.paypal.com. Open Developer Tools again and you’ll see the browser goes directly to the encrypted https://www.paypal.com, HSTS in action! Now open a new tab and go to paypal.com (without the www) again. You’ll see this time Developer Tools shows there’s still an unencrypted 301 redirect, meaning an attacker can still get you with a MITM attack. This is a perfect example of the canonical redirect pitfall with HSTS. Another site which has the exact same problem is http://pixi.me. My guess is this error will repeat itself many times once more sites start implementing HSTS.
This issue doesn’t seem to be readily apparent to a lot of people or at least they don’t mention it. Even people who should probably be the most aware of it. Here’s an except from the Security Now podcast by Steven Gibson talking about Paypal HSTS:
“So, for example, if the user put in just http://www.paypal.com, or even http://paypal.com, if there is a Strict Transport Security token that has been received previously, the browser has permission to ignore that non-secure query request from the user and to transparently make it secure.”
And after reading Paypal’s HSTS announcement you’d assume that typing in paypal.com would be secure too.
To fix this we must set an HSTS policy on both yoursite.com and http://www.yoursite.com. Here’s how:
1) Setup a 301 redirect from http://yoursite.com to https://yoursite.com
2) Setup a 301 redirect from https://yoursite.com to https://www.yoursite.com
3) Set an HSTS header on the redirect (or all responses) from https://yoursite.com
4) Setup a 301 redirect from http://www.yoursite.com to https://www.yoursite.com
5) Set an HSTS header on all responses from https://www.yoursite.com
So the redirect chains will look like this:
And the request response cycle will look like this (for the first redirect chain):
- browser requests http://yoursite.com
- server redirects to https://yoursite.com
HTTP/1.1 301 Moved Permanently Location: https://yoursite.com
HTTP/1.1 301 Moved Permanently Location: https://www.yoursite.com Strict-Transport-Security: max-age=2592000
- browser stores HSTS policy for yoursite.com and will never connect to it without encryption for a month
- browser requests https://www.yoursite.com
- server sets HSTS header and serves up standard homepage
HTTP/1.1 200 OK Strict-Transport-Security: max-age=2592000 Content-type: text/html ...
- browser stores HSTS policy for http://www.yoursite.com and will never connect to it without encryption for a month
Now you have secured both users who type yoursite.com into the location bar and those who type http://www.yoursite.com. The downside to this is that you require two HTTPS connection which will be slower for the user and more expensive for your servers. You’ve also still not secured the user who typed in http://www.yoursite.com and then a week later typed in yoursite.com. That user’s first ever connection to yoursite.com will still be open to a MITM attack.
To secure both yoursite.com and http://www.yoursite.com on the initial connection to either here’s what you would have to do. First make sure you have the simple 301 redirects from http://yoursite.com or https://yoursite.com to https://www.yoursite.com as most sites already have done. Then embed an invisible iframe on the landing page (or every page) of https://www.yoursite.com which loads https://yoursite.com/hsts. This /hsts path would do nothing other than send back a blank page with the HSTS header set. The /hsts response could also contain cache headers (Expires and Cache-Control: public) which would make the browser not re-request it for some amount of time which is less than your HSTS policy. This still has the issue of requiring two HTTPS connections every time a user types in yoursite.com.
How you actually implement any of the previous concepts depends entirely on your web server and framework. This post is just meant to get the idea out there and people aware of this potential pitfall. See the HSTS Wikipedia article for some implementation examples of HSTS.
Considering site.com -> http://www.site.com canonical redirects are quite ubiquitous across the web it seems like something HSTS should deal with better. They include an HSTS option called includeSubDomains which would handle http://www.site.com -> site.com canonical redirects. But even that isn’t perfect because it would also enforce the HSTS policy on other possibly unimportant subdomains like blog.site.com.
Solution 3: Add a Hack
I wonder if it’s worth putting an explicit exception into HSTS which allows the www subdomain to set its parent domain’s HSTS policy. Maybe only allowing it to set or increase the TTL of the policy, and never allowing it to decrease. I think this would make HSTS much easier to implement for the vast majority of websites.
In my next post I’ll suggest some potential additions to HSTS which could make things more simple for implementers.
Thanks to Victor Costan for reviewing this post and helping me brainstorm.