1. Introduction
The Chrome Isolated Origins specification enables apps to opt-in to an environment that is isolated from other web applications in the same desktop session. The isolation is intended to prevent cross-site scripting (XSS) and other web-based attacks.
This document aims to identify and define the best possible solution to the following open question from an early version of the Isolated Origins specification:
How should navigations be limited? Limiting navigations breaks linkability and hinders functionality, but improves defense against reflected XSS and CSRF. Striking the right balance here will be tricky. We could only allow navigations to the root URL on the isolated origin, or we could transform navigations into a navigation to the root URL with some sort of message to allow the site to decide whether the navigation should go through. Or foreign fetch could be modified to allow the isolated origin’s Service Worker to intercept the navigation and decide whether it should be allowed.
2. Potential solutions considered
-
Only allow navigations to the top-level page.
-
Allow navigations to the top-level page, also allowing navigations to arbitrary paths sans query params / fragments.
-
Flexible navigation manager implemented by the isolated application
3. Factors being considered in the selection of a preferred solution
-
Any solution should leverage existing web technologies to the maximum extent possible so as to avoid reinventing the wheel.
-
XSS in paths (without querystring data).
A problem with solution 3. -
Allowing navigation only to a top level page is arbitrarily restrictive.
A problem with solution 2. -
Generally speaking, input to an isolated web app should go through a formal API that can be appropriately regulated based on data about the input. (Consider the benefit of postMessage vs. legacy message passing mechanisms.)
A problem with solution 3. -
Should avoid TOCTOU problems and list maintenance issues.
A problem with solution 1. -
Should avoid hampering deep linking.
A problem with solutions 1, 2, and 3, but likely not 4. -
With any approach, it’s fair to say that the goal is to centrally regulate data coming into the application via navigations at the client, in a way that is "secure by default."
Not a problem with any approach being discussed. -
The mailing list unsubscription scenario needs to work. That is, the case where a link in e-mail takes the user to the isolated app and passes some data to the app.
A problem with solutions 2 and 3. -
CSRF via cross-origin POST.
Out of scope as this is addressed more generally by Isolated Origins. -
Secure by default.
A drawback of solution 3. -
Flexible enough to allow for the development of full-featured isolated web applications.
4. Preferred solution
The navigation manager approach appears to be most preferable when considering the list of factors above. The biggest drawback of the navigation manager approach is that it relies on the isolated app not to implement a navigation manager carelessly. But navigation managers should be easy to audit, and secure sample implementations may be provided. It would be nice to see popular open-source navigation managers emerge.
5. Proposed navigation manager mechanism
Without an implemented navigation manager, only basic HTTP GET based navigations to the root path of an isolated app would be allowed. This would let malicious sites spawn an instance of the malicious app in a new window, but nothing more. (Framing will be prohibited by Isolated Origins.)
To avoid re-inventing the wheel, the navigation manager mechanism would leverage
the existing browser support for Service Workers and Fetch Events.
To implement a navigation manager, an isolated origin would first install a third-party service worker. Next it would call registerNavigationManager()
. (This concept is based upon registerForeignFetch()
.)
Example call to registerNavigationManager()
:
self.addEventListener('install', event => {
event.registerNavigationManager();
});
It should not be possible to register a navigation manager outside of the isolated environment. Otherwise it may be possible for a non-isolated site to register a navigation manager that is then enforced once the origin becomes isolated.
The isolated origin should generally fail to navigate unless a registered navigation manager is consulted. The only exception would be for HTTP GET-based navigations that do not include path or querystring data. This restriction helps ensure that the system overall fails secure by default.
Subsequent to navigation manager registration, the service worker would set up
a navigationsafetycheck
event handler. The navigationsafetycheck
event as
detailed below does not need to fetch the resource itself. It has access to the
request and can make a decision that the browser will then consider as it
fetches the resource in question.
Example navigationsafetycheck
event handler:
self.addEventListener('navigationsafetycheck', event => {
var result = { method: event.methods.BLOCK, withCredentials: false};
if (event.request.method == 'GET') {
var url = new URL(event.request.url);
if (url.protocol == 'https:') {
if ((url.pathname == '/') && (url.search ==
'?') && (url.hash == '#')) {
result.method =
event.methods.ALLOW;
} else {
result.redirectUrl =
"https://" + url.hostname + "/";
result.method =
event.methods.REDIRECT;
}
}
}
event.respondWith(result);
});
The navigationsafetycheck
event handler will be called for any resource
request that is in scope for navigaiton management. This means the following
conditions are satisfied:
-
The resource request is a navigation. (As opposed to a non-navigational resource request.)
-
The resource request is in-scope for the service worker.
-
The resource request is in the origin of the isolated app.
-
The navigation was initiated by a source external to the isolated origin. Navigations from the same origin that are initiated outside of the isolated origin environment are considered external.
Navigations initiated from outside of an isolated origin can not target a frame within the isolated origin.