1. Introduction
Authors of security-critical applications want the web’s low-friction install process, its linkability, and its cross-platform nature, but are understandably nervous about hosting very powerful applications on the web alongside malicious content that can attack in any number of ways. For example, malicious content can:
-
Exploit cross-site scripting (XSS) or cross-site request forgery (CSRF) vulnerabilities to steal sensitive user data or take actions on a user’s behalf.
-
Trick the user into revealing data or taking actions via clickjacking attacks.
-
Navigate a security-critical application to a phishing page without the user’s knowledge, and then trick the user into leaking sensitive data.
It may not make sense to describe Isolation as mitigating phishing scenarios like this. It may indeed do so, but it’s not the primary goal.
-
Leverage a browser vulnerability to gain access to data from other origins (universal XSS or UXSS).
Security-critical applications should be able to run in a browser with a degree of isolation that mitigates these attacks. This specification defines an isolation mechanism that an origin can opt in to, thereby reducing its exposure to other web content. Developers responsible for security-critical web applications can choose to trade away some of the features of being on the open web (such as full linkability) in exchange for better protection from malicious web content.
Given that a navigation manager enables linkability, it may not make sense to suggest there is a necessary tradeoff, at least in this respect.
1.1. Threat Model
We consider a security-critical application, very-important.example
, whose developers want to
isolate it from regular web content like evil.example
. We assume that very-important.example
is
a large, complicated site with many developers, some of whom occasionally make mistakes, such as
accidentally opening up a reflected XSS vulnerability. We also assume that the user does not know
that evil.example
is evil, and also that the user is not necessarily a power user; thus the user
does not necessarily take precautions such as using evil.example
and very-important.example
in
separate profiles or browsers. In our threat model, evil.example
might try to attack very-important.example
in a number of ways which this document aims to address:
-
evil.example
might cause the user’s browser to send a request tovery-important.example
that exploits a cross-site scripting (XSS) or cross-site request forgery (CSRF) vulnerability. Such vulnerabilities might be triggered by navigational requests (viaiframe
oropen()
), or by subresource requests (viaimg
, etc). -
very-important.example
might open a link toevil.example
(for example, user-generated content could include an auto-linked reference to that origin, or a content write posted a link toevil.example
without knowing that it is evil). This allowsevil.example
to obtain a reference to thevery-important.example
window via thewindow.opener
attribute, which opens up communication channels (postMessage()
, frame counting and traversal, navigation of thevery-important.example
window, etc.) thatvery-important.example
might not be expecting. -
Likewise,
evil.example
can obtain such a reference viaopen()
. -
evil.example
might exploit a vulnerability in the browser itself (UXSS) to gain access tovery-important.example
data and cookies.Note: The mechanism proposed in this document does not aim to give hard guarantees about protection from UXSS, but does hope to mitigate some of the risks on a best-effort basis.
-
evil.example
might be able to extract data fromvery-important.example
by loading its resources or framing its content. Extraction techniques could range from exploitation of overly-broad CORS headers, to timing attacks like [HEIST], [PIXEL-PERFECT], etc.
Maybe mention that this would be facilited by ambient authority?
-
very-important.example
might embed resources from other origins (e.g.,investments-r-us.biz
), where the user becomes authenticated withinvestments-r-us.biz
by authenticating withvery-important.example
.evil.example
might then be able to extractvery-important.example
user data frominvestments-r-us.biz
, even if it can’t attackvery-important.example
directly. -
evil.example
andvery-important.example
might in fact beevil.something.example
andvery-important.something.example
, in which case:-
evil.something.example
might try to set a cookie for all ofsomething.example
in hopes that it can fix a session or CSRF token for the user onvery-important.something.example
. -
evil.something.example
might take advantage ofvery-important.something.example
's (ill-advised) loosening of the same-origin policy viaDocument
'sdomain
attribute.
-
1.2. Mitigation Strategy
To address the threats above, this document details an origin-wide isolation mechanism that aims to
break the communication channels between very-important.example
and evil.example
. When very-important.example
opts-in to this mechanism, the user agent will enforce the following:
-
Resources from
very-important.example
may not be framed by cross-origin contexts. That is, the user agent will act as thoughContent-Security-Policy: frame-ancestors 'self'
is delivered with each resource. This preventsevil.example
from exploiting attacks that require control oververy-important.example
's browsing context ([PIXEL-PERFECT], as well as straightforward clickjacking), or access tovery-important.example
'sWindow
(viacontentWindow
). -
Cross-origin resources framed by
very-important.example
are not given access towindow.top
orwindow.parent
(those accessors will returnnull
). This preventsevil.example
from exploiting attacks which require access tovery-important.example
'sWindow
, or navigating the user to an unexpected URL to replacevery-important.example
in the top-level browsing context. -
When
very-important.example
opens a new, cross-origin window (viaopen()
,<a target="_blank">...</a>
, etc.), the new window will disown its opener, and the call toopen()
will returnnull
. That is, the user agent will act as though noopener is specified on outgoing links andopen()
calls. -
All cookies set by
very-important.example
will be forced to be strict same-site cookies. This preventsevil.example
from initiating authenticated requests tovery-important.example
. Further, all cookies set onvery-important.example
(including cookies set by responses to cross-origin requests) will be double-keyed and not sent on requests that occur outside the isolated context. This preventsevil.example
from attacking ambient authority granted tovery-important.example
's cross-site dependencies by virtue of being embedded invery-important.example
. -
Navigation into the isolated origin will be regulated by a navigation manager. Code from the isolated origin will get to validate each navigation before allowing it to proceed.
-
The user agent should load
very-important.example
into a separate process from other web content, including cross-origin content that it embeds, if possible.Note: This is not a firm guarantee (especially since not all browsers implement per-site process isolation at all), but rather a best-effort attempt to mitigate the risks of UXSS bugs.
2. Key Concepts
An isolated origin is an origin that has opted in to isolate itself from other origins on the web, using the mechanisms defined in this specification.
User agents have an isolated origin store, which is a list of isolated origins.
When asked if an origin is isolated, an isolated origin store will return true if the given origin appears in its list, and false otherwise.
Expand to have an expiration time for each isolated origin in the store.
3. Opting into isolation
Isolated origins are defined by an isolation HTTP response header. The syntax for the name and value of the header are described by the following ABNF grammar [RFC5234]:
"Isolation:" *WSP "1" *WSP
§5.3.2 Processing isolation headers describes how the isolation header is processed.
the isolate-me HTTP header needs to be sent and/or processed before the Set-Cookie header...
4. Controlling navigations
An isolated origin can use a service worker to perform security checks on navigations. By default, every navigation to an isolated origin fails, unless the origin has a service worker which explicitly allows it.
§5.3.1 Navigation checks and §5.1.5 Process a navigate response describe how the Fetch and HTML specs are modified to fail navigations to an isolated origin unless a service worker has performed security checks on them.
5. Integrations
5.1. Integration with HTML
5.1.1. Origin
The HTML concept of origin should be expanded to contain a boolean isolated getter. This getter returns true if the origin is isolated and false otherwise.
The same origin check should be modified to return false if A
is isolated but B
is not, or vice versa.
Justify why the isolated flag does not need to be serialized
5.1.2. Cross-origin framing of isolated origins
Isolated origins should not be framed by cross-origin content. Step 1 of Process a navigate response should be modified to call §6.1 Should navigation response in target be blocked?.
5.1.3. Disowning isolated openers
When an isolated origin opens a new window to a different origin, that origin should not be able to
manipulate the isolated origin via the window.opener
attribute.
Initializing a new Document object should be modified to call §6.2 Disown an isolated opener on browsingContext and the response used to generate the document.
5.1.4. Removing cross-origin subframe references
When an isolated origin embeds cross-origin subframes, those subframes should not be able to obtain
references to the isolated origin’s window via window.top
or window.parent
.
The window.top
attribute algorithm should be modified to return
null if context
's top-level browsing context’s active document’s origin is an isolated origin.
Similarly, the window.parent
attribute algorithm should be
modified to return null if context
's top-level browsing context’s active document’s origin is an isolated origin.
5.1.5. Process a navigate response
Process a navigate response should be modified to fail a navigation to an isolated origin if it has not been explicitly allowed by a service worker for that origin. Specifically, Step 1 should be modified to include the condition:
-
response’s url’s origin is an isolated origin and request’s allow isolated navigation flag is unset.
5.2. Integration with Cookies
This section defines modifications to [COOKIES] to support isolated origins.
5.2.1. Forcing cookies to be same-site
Forcing cookies from isolated origins to be same-site means that an attacker cannot initiate an authenticated request from a victim’s browser to an isolated origin.
When receiving a cookie string on a request whose initiator’s origin is isolated, if the request’s origin is same origin with the
initiator’s origin, Section 5.3 should be modified to set the cookie’s samesite-flag
field to "Strict".
5.2.2. Double-keyed storage
When a cookie is set on a request for which the top-level document is an isolated origin, the cookie should be double-keyed, such that the cookie is only sent on requests initiated by contexts in which the top-level document is the same isolated origin. This prevents attackers from attacking ambient authority that an isolated origin’s cross-site dependencies obtain by virtue of being embedded in the isolated origin.
This section defines modifications to [COOKIES] to support these double-keyed cookies.
5.2.2.1. Storage model
The storage model of [COOKIES] should be updated to keep track of when cookies are set in a
top-level isolated origin as follows. Section 5.3 of [COOKIES] should be modified to store an
additional isolated-origin
field for each cookie. When processing a cookie, if the request has a non-null client, then let top origin
be the origin of
the request’s client’s responsible browsing context’s top-level browsing context’s active Document. If top origin
is an isolated origin, then then the user agent should set the isolated-origin
cookie field to top origin
. Otherwise, the user agent should set the isolated-origin
cookie
field to the empty string.
define this section for Worker-based requests
5.2.2.2. The Cookie header
[COOKIES] should be updated to ensure that cookies that were set on an isolated origin are not sent in other contexts outside that origin. In other words, Section 5.4 of [COOKIES] should be modified to add the following to the list of requirements in step 1:
-
If the cookie’s
isolated-origin
field is not the empty string, then the request must have a non-null client whose responsible browsing context’s top-level browsing context’s active Document’s origin is an isolated origin and is same origin with the cookie’sisolated-origin
field.
these cookie changes don’t prevent all session fixation attacks. If a user has never visited victim.example.com before, evil.example.com can set a cookie for .example.com, which will be sent on the request when the user first navigates to victim.example.com. The isolated origin can mitigate this by using __Host- cookies for sensitive cookies. Maybe isolated origins should be required to use __Host- on all cookies.
5.3. Integration with Fetch
5.3.1. Navigation checks
The Fetch spec should be modified to give request a boolean allow isolated navigation flag, which is false by default. This flag is checked on navigations to isolated origins in §5.1.5 Process a navigate response.
The Request class should be modified to have an allowIsolatedNavigation()
method, which, when invoked, must run
these steps:
-
Let origin be current settings object’s origin.
-
If origin is not same origin with request’s current url’s origin, then abort these steps.
-
Set request’s allow isolated navigation flag to true.
NOTE: A service worker or a consumer of the Fetch API must call allowIsolatedNavigation() on a Request in order for a navigation to an isolated origin to be allowed.
Does this allow for navigation requests to redirect to isolated origins? Do we want it to?
5.3.2. Processing isolation headers
Fetch should be updated to call §6.3 Isolate an origin on response after Step 13 of HTTP-network fetch.
6. Algorithms
6.1. Should navigation response in target be blocked?
This algorithm is used to block cross-origin framing of isolated origins. Given a response (navigation response) and a browsing context (target), this algorithm returns "Blocked
"
if navigation response is from an isolated origin and one or more of the ancestors of target is
cross-origin.
-
If target is not a nested browsing context, return "
Allowed
". -
If navigation response’s url’s origin is not an isolated origin, then return "
Allowed
". -
Let current be target.
-
Let navigation origin be a copy of navigation response’s url’s origin. Set navigation origin’s isolated flag to true.
-
While current has a parent browsing context (parent):
-
Set current to parent.
-
Let origin be current’s active document’s relevant settings object’s origin.
-
If origin is not same origin with navigation origin, return "
Blocked
".
-
-
Return "
Allowed
".
6.2. Disown an isolated opener
Given a browsing context (browsingContext) and a response (response) used to generate a document, this algorithm disowns browsingContext’s opener if the opener is an isolated origin.
-
If browsingContext does not have an opener browsing context, then abort these steps.
-
Let openerOrigin be browsingContext’s opener browsing context’s active document’s relevant settings object’s origin.
-
If openerOrigin is not an isolated origin, abort these steps.
-
If openedOrigin is same origin with openerOrigin, then abort these steps.
-
browsingContext should disown its opener.
6.3. Isolate an origin
This algorithm is called from HTTP-network fetch to process an isolation header on a response response.
-
If extracting header list values given isolation and response’s header list returns null, then abort these steps.
-
If origin is not an isolated origin, set request’s allow isolated navigation flag to true.
NOTE: This step ensures that the first navigation to an isolated origin is allowed.
-
Add origin to the user agent’s isolated origin store.