Death by copy/paste


HSTS and preloading

For those of you unfamiliar with HSTS and preloading I have a couple of blogs to get you started, there's HSTS - The missing link in Transport Layer Security and HSTS Preloading. The TL;DR is that HSTS is a HTTP response header you can set on your site to force the browser to use HTTPS all the time and HSTS Preloading is where you request browsers to actually bake this setting into their source code so it's incredibly difficult to remove. This is a huge boost to security but you have to be sure that you can serve your site, and every subdomain, over HTTPS to preload, or users won't be able to access them. Common problems include people not considering internal subdomains that use HTTP and subdomains maintained or managed by 3rd parties. These break completely once the site is preloaded so the last thing you'd want is your site to be preloaded if you weren't ready.

Copy/paste configs

During my related research I noticed that a lot of websites were using the HSTS header with the preload token present, indicating they wish to be HSTS preloaded by browsers. I thought the number of sites including the token was odd, it seemed too high. It turned out I was right. I ran the list of sites against the HSTS preload site's API to check the status and eligibility for preloading. It turned out that a lot of them were including the token, but were not actually preloaded, meaning they hadn't submitted themselves to the preload list. It seems odd to set the token to indicate you want to be preloaded but then not request to actually be preloaded. What was even more odd was that the majority of them failed the criteria to get on the preload list anyway. You can check status and eligibility using the API here:

These results were coming in whilst I was chatting to Bryant Zadegan on Skype about his recent talk at BH USA and DEF CON about using a similar feature to HSTS, HPKP, for malicious purposes and he realised what it was. The most popular configs that people were using were copied and pasted from various guides on the Internet! This was quickly verified by simply pasting some of the most common configs in Google and finding the source.

The biggest offenders

The data I used here was taken from my crawl of the Alexa Top 1 Million so it is very recent data. I took the list of all sites that issued the Strict-Transport-Security header and re-crawled them to extract the value of the header. I reduced this list to just those that included the preload token and then ran it through the status API listed above. If the site was reported as preloaded then I removed it from the list, if not then I logged it and stored the header value. Here is a trimmed list of results:

max-age=63072000; includesubdomains; preload - 1045
max-age=31536000; includesubdomains; preload - 730
max-age=15552000; includesubdomains; preload - 573
max-age=31536000; preload - 256
max-age=15552000; preload - 254
max-age=63072000; preload - 249
max-age=0; includesubdomains; preload - 179
max-age=15768000; includesubdomains; preload - 105
max-age=10886400; includesubdomains; preload - 98
max-age=16000000; includesubdomains; preload; - 79
max-age=0; preload - 68
max-age=2592000; preload - 61
max-age=2592000; includesubdomains; preload - 56
max-age=15768000; preload - 41
max-age=31536000; includesubdomains; preload; - 34
max-age=15552001; preload - 28
max-age=31536000;includesubdomains;preload - 20
max-age=15768000; includesubdomains; preload; - 17
max-age=3600; preload - 17
max-age=631138519; preload; - 15
max-age=15552001; includesubdomains; preload - 14
max-age=16000000; preload; - 14
max-age=31536000; preload; - 13
max-age=31536000;preload - 13
max-age=10886400; preload - 13
max-age=16070400; includesubdomains; preload - 12
max-age=15724800; includesubdomains; preload - 11
max-age=86400; includesubdomains; preload - 10
max-age=7776000; includesubdomains; preload - 10

In total there were 4,333 sites in the Alexa Top 1 Million that were indicating that they wished to be preloaded but were not actually preloaded! This is a huge number considering that only 29,908 sites issue the HSTS header, 14.5% have preload but aren't actually preloaded. Remember, these figures don't include sites that are preloaded, only ones that aren't. So, how did these preload tokens get in so many headers?

max-age=63072000; includesubdomains; preload (x1,045)

A quick bit of Google fu lead me straight to this article from KeyCDN:
The Apache and NginX config examples in that article are an exact match.

max-age=31536000; includesubdomains; preload (x730)

Next up was the HSTS Cheat Sheet from OWASP:
This value is under their Recommended heading but Jim Manico updated this to include a warning very quickly after I got in touch on Twitter. Here is the actual diff.

This config is also recommended by the United States Goverment:

max-age=15552000; includesubdomains; preload (x573)

This one seems to be down to CloudFlare:
They allow sites to enable HSTS with various preconfigured options and the highest value for their max-age is 15552000.

max-age=10886400; includesubdomains; preload (x98)

The HSTS Preload site itself gives an example header configuration:
This is the HSTS preload site after all, so I guess I can see why they suggest it.

This could destroy your site

The one thing that's saving this from being a complete disaster is that the HSTS Preload submission site was recently overhauled and now has some checks that you have to pass before you are added. This is what the "preloadable" API call above does. A lot of the sites setting the preload token in their header actually fail these checks so couldn't be submitted. The problem is that some of them do pass the checks. You could actually go to the HSTS Preload site right now and request that these sites be included for preloading. They've already given their consent to being preloaded by including the token in their header and this could cause some real problems. You wouldn't know until the preload actually hit mainline Chrome and then your entire domain is HTTPS or nothing. Giving someone else the ability to pull the trigger on something like this is a bad idea.

If you want to see the full list I uploaded it to PasteBin here: