1. Introduction
This section is not normative.
Migrating sites onto HTTPS is difficult. It has been our common understanding that these difficulties were wrapped up in server configuration, performance, and certificate maintenance. It’s recently become clear that the true challenges lie elsewhere. In particular, opting into secure transport comes with a number of restrictions which ensure that "secure" really means something. Mixed Content in particular can block an upgrade entirely. Brad Hill has laid out an excellent explanation of the challenges that mixed content blocking presents, and pointed out a number of situations in which cross-dependencies between insecure Origin A and Origin B might cause real deadlock where no one is able to upgrade without breakage. His "Why isn’t HTTPS everywhere yet?" document [TRANSITIONAL] explains the situation quite well, and proposes one approach to solving the problem. This document proposes another, which is smaller but simpler.
Today, the mechanism outlined in [UPGRADE-INSECURE-REQUESTS] allows a site
to make assertions on behalf of the resources it loads that they ought to be
requested over secure transport. If we provide a similar mechanism to those
resources' owners, then that new mechanism could be used to break deadlocks by
allowing Origin B to upgrade to HTTPS, and assert that requests from Origin A
should be upgraded rather than blocked as mixed content. [RFC6797] introduced the Strict-Transport-Security
header, which looks like it could
be that mechanism.
HSTS, however, is currently checked after mixed content blocking has already happened. This is due to the fundamental property that HSTS takes effect only after a user’s agent has requested a resource from a host that sends the header. The effect is therefore determined by the vagaries of a particular user’s browsing history. If she visits Origin B before Origin A, then Origin A’s resources could be upgraded without issue. If she visits Origin A first, then those resources won’t be upgraded, and the site will break in some interesting way.
Origin A’s developer, of course, is overwhelmingly likely to have visited Origin B, given the dependencies. This means that they are both extremely unlikely to experience the failure, and extremely likely to fall into the "Works on my machine!" trap when dealing with user’s bug reports. Users would be sad, developers would be confused, and the world would collapse into an unfortunately indeterminate pile of partially broken sites.
It doesn’t have to be this way, however. This document proposes updates to [FETCH], [MIXED-CONTENT], and [RFC6797] which introduce the concept of an HSTS priming request. This request will attempt to validate an insecure resource’s host’s HSTS policy before blocking it as mixed content, therefore allowing any relevant upgrade from HTTP to HTTPS to be made in a deterministic way for all users.
This is not a recommendation-track document, it simply intends to explain the notion of HSTS priming, and gather up relevant changes in a single place for discussion. The normative changes will happen in the relevant documents.
1.1. Example
Origin A contains the following HTML:
<script src="http://origin-b/widget.js"></script>
Before blocking this insecure request as mixed content, the user agent performs an HSTS priming request to the root of the resource’s host, as follows:
HEAD / HTTP/1.1 Host: origin-b ...
Origin B sends back a response which triggers HSTS for the host:
HTTP/1.1 200 OK ... Strict-Transport-Security: max-age=10886400; includeSubDomains
The user agent can then safely upgrade the initial request from HTTP to HTTPS, rather than blocking it as mixed content.
1.2. Goals
HSTS priming has the overarching goal of removing mixed content as a barrier to securing the internet. This breaks down into two concrete objectives:
-
Convert the
Strict-Transport-Security
assertion from a negative one ("This host is only available via secure transport") to a positive one ("Any resource on this host addressed by an insecure scheme is available via secure transport. Please grab it from there instead."). -
Teach user agents how to verify this new HSTS assertion early enough in a request’s lifecycle to avoid being blocked as mixed content.
2. Proposed Modifications
2.1. Fetch
2.1.1. Main Fetch Modifications
-
After the current step 3 (which deals with
Upgrade-Insecure-Requests
), add the following steps:-
If request's client prohibits mixed security contexts, and request's current url’s scheme is "
http
" or "ws
", and it isn’t a navigation request, and its host does not match the HSTS cache, then perform an HSTS priming request for request.
-
-
Move the current step 6 (which deals with HSTS) before the current step 4 (which deals with CSP and mixed content).
Note: This isn’t entirely accurate, as we probably want reporting to happen before the upgrade (that is, before the current step 3). That’s a bug in the existing algorithm that I need to take care of.
Note: Maybe use the newly defined HSTS cache for the upgrade instead of deferring to the RFC.
Note: If a user agent implements prerendering or prefetching, it might be a wonderful idea to perform a priming request for any insecure navigation that such a function proposes. The performance impact would be (mostly) hidden by the background nature of such a request, and the security impacts would be interesting indeed.
2.1.2. HTTP-network Fetch modifications
Modify HTTP-network Fetch as follows:
-
After the current step 9 (which processes the
Set-Cookie
header), execute the following steps:-
If response's header list contains one or more headers named
Strict-Transport-Security
, then add an entry to the HSTS cache with the following properties:-
"
upgrade
"
-
2.1.3. HSTS Priming Requests
To perform an HSTS Priming Request for request, run these steps:
-
If request's current url’s scheme is not "
http
" or "ws
", abort these steps.Note: I’m assuming that Web Sockets are going to be folded into Fetch at some point?
-
Let primer be a new request whose properties are set as follows:
-
"
HEAD
" -
A new URL whose scheme is "
https
", host is request's current url’s host, and port is request's current url’s port -
request's initiator
-
request's type
-
request's destination
-
request's credentials mode
-
"
manual
" -
null
-
"
no-referrer
"
-
Append a header named "
Upgrade-Insecure-Requests
" with a value of "1
" to primer's header list. -
Let response be the result of performing an HTTP-network-or-cache fetch using primer.
-
If response's header list does not contain one or more headers named
Strict-Transport-Security
, then add an entry to the HSTS cache with the following properties:-
A reasonable expiration. Perhaps 24 hours?
-
"
exclude
" -
"
do not upgrade
"
2.1.4. HSTS Cache
An HSTS cache consists of a collection of HSTS cache entry objects, each with the following properties:
-
A host
-
A timestamp, after which this entry is no longer valid
-
"
include
" or "exclude
" -
"
upgrade
" or "do not upgrade
"
2.1.5. Does request match the HSTS cache?
A request (request) matches the HSTS cache if there exists an HSTS cache entry entry in the user agent’s HSTS cache such that:-
The current time is not after entry's expiration
-
entry's host is a concurrent match for request's current url’s host
or
entry's subdomains is "
include
", and entry's host is a superdomain match for request's current url’s host
2.2. Mixed Content
-
Explain the nature of the relationship to HSTS.
2.3. RFC 6797
-
Explain the nature of the new assertion that’s being made, and its implications.
-
Integrate the spec with Fetch.
3. Open Questions
-
Would it be valuable enough to do this for navigational requests as well? That would have the impact of making the initial window for attack marginally smaller (e.g. the attacker would have to actively block connections over 443, rather than executing an ssl stripping attack), but it’s not clear that there’s much value in the additional complexity.
4. Security Considerations
HSTS priming makes it more likely that developers can upgrade their sites from HTTP to HTTPS, as discussed in the introduction.
It has the nice side-effect of making it possible for CDNs and widget
providers to increase the security of the web in one fell swoop, which
increases the value of the Strict-Transport-Security
header significantly.
5. Privacy Considerations
5.1. Leakage via Divergent Hosts
As little sense as it may make, some hosts do serve different content via
HTTPS than they serve via HTTP (forbes.com
is the canonical example). HSTS
priming has the potential of leaking information about insecure resource
requests to the secure variant of the requests' host. We mitigate this risk
by stripping out interesting information from the priming request. It does
not contain referrer information (as we set the referrer policy to none
), it
contains no credential information, and it doesn’t contain the URL of the
resource being requested (as we target the root of the secure origin).
5.2. History Leakage
The HSTS state of a host leaks some information about a user’s browsing history. Likewise, if the user agent caches failed HSTS priming requests, those failures leak information. User agents SHOULD give users the ability to clear these caches, and should expire them in a reasonable amount of time.
6. Acknowledgements
Richard Barnes proposed this treatment in a thread that included very helpful comments from Eric Mill, Brad Hill, Mark Nottingham, Brian Smith, Tanvi Vyas, Martin Thomson, and Anne van Kesteren.