30 August 2016

Fun with HTTP Strict Transport Security!

At the moment I write this, something odd is happening with the US Senate site (if you view it in Chrome at least). If you go to http://www.senate.gov/, you will see the home page.


But, if you go to https://www.senate.gov/, you will see an error message telling you that HTTPS isn't working, and a link to go back to HTTP instead.





Clicking the link - which does actually point to HTTP instead of HTTPS - will, however, cause the browser to re-request https://www.senate.gov/, which of course you can't get to because of the error. So, your next diagnostic step might be to close your browser and reopen it, then explicitly enter http://www.senate.gov/. This will cause your browser to AGAIN request https://www.senate.gov/ instead of http://www.senate.gov/. What gives?

The cause of this behavior is something called HTTP Strict Transport Security, or HSTS. I ran into a problem with HSTS a few months back, but the problem was on an internal site that I couldn't share in a blog post.

Basically, HSTS tells browsers to ONLY use HTTPS for some period of time after the first HTTPS request. The initial HTTPS response will include a response header called, appropriately enough, Strict-Transport-Security. This header will contain a max-age value in seconds, and for the duration of that time, the browser will only send HTTPS requests to the server. Any HTTP links in documents from that server will automatically be treated as if they were HTTPS requests. The odd thing here, arguably, is that the response header can actually be sent back with an HTTP response, and will have no effect until an HTTPS request is made, at which point all subsequent requests must be HTTPS until the time has elapsed. This time period may be quite long - in the screenshot below, it's one hundred and eighty days!

Another feature used to enforce HTTPS usage is called Upgrade Insecure Requests. This is often used with HSTS. It's a Content Security Policy that servers can ask browsers to use in their initial HTTP response headers, or that browsers can use without prompting. If a browser supports it, it'll send a corresponding HTTP request header:

Upgrade-Insecure-Requests: 1

This will allow the server to serve an HTTPS alternative if one exists. Modern versions of Chrome and Firefox do this for every page request, but Microsoft Edge does not. Here's a screenshot showing the server Content-Security-Policy directive and Strict-Transport-Security response headers, and the Upgrade-Insecure-Requests request header.


Each of these is a good and valuable feature for improving HTTPS security, but together they can cause some odd problems. The problem I ran into, specifically, was a combination of issues. Note the attributes of Strict Transport Security in the previous screenshot. Not only is there an age, but also an "includeSubdomains" directive.

Let's say you have http://someapp.domain.com/. And on that site, you include a CSS file that's used by the "main" site http://domain.com/. This is a pretty common practice. Now, let's say that HTTPS is enabled on domain.com. If you use Chrome or Firefox, they will read the HTTP URL from the default document on someapp.domain.com, then send an HTTP request for that URL to domain.com - along with the Upgrade-Insecure-Requests header. That server will then redirect the browser to the HTTPS version of that URL, and send back the Strict-Transport-Security response header.

On your next page request to http://someapp.domain.com/, Chrome or Firefox will then automatically request https://someapp.domain.com/ instead! This will cause a problem if you don't actually have a TLS certificate installed on your server. You can address this by (a) adding a valid, signed TLS certificate to your server, or (b) removing requests to external servers that support HTTPS within the same domain.

If you use HTTPS everywhere, which is strongly recommended, you won't have this problem. Until then, be careful!

[Note: cross-posted on the Fig Leaf Software blog]

No comments:

Post a Comment

All comments are subject to potentially unfair moderation. All comments are owned by the poster of said comments.