Suborigins

Editor’s Draft,

This version:
https://w3c.github.io/webappsec-suborigins/
Feedback:
public-webappsec@w3.org with subject line “[suborigins] … message topic …” (archives)
Issue Tracking:
GitHub
Inline In Spec
Editors:
Joel Weinberger (Google Inc.)
Devdatta Akhawe (Dropbox Inc.)
(Google Inc.)

Abstract

This specification defines a mechanism for programmatically defining origins to isolate different applications running in the same physical origin. It allows a server to specify a namespace on a resource response which is paired with the scheme/host/port origin tuple. User agents extend the same-origin policy with this new namespace plus origin tuple to create a security boundary between this resource and resources in other namespaces.

Status of this document

This is a public copy of the editors’ draft. It is provided for discussion only and may change at any moment. Its publication here does not imply endorsement of its contents by W3C. Don’t cite this document other than as work in progress.

Changes to this document may be tracked at https://github.com/w3c/webappsec.

The (archived) public mailing list public-webappsec@w3.org (see instructions) is preferred for discussion of this specification. When sending e-mail, please put the text “suborigins” in the subject, preferably like this: “[suborigins] …summary of comment…

This document was produced by the Web Application Security Working Group.

This document was produced by a group operating under the 5 February 2004 W3C Patent Policy. W3C maintains a public list of any patent disclosures made in connection with the deliverables of the group; that page also includes instructions for disclosing a patent. An individual who has actual knowledge of a patent which the individual believes contains Essential Claim(s) must disclose the information in accordance with section 6 of the W3C Patent Policy.

This document is governed by the 1 March 2017 W3C Process Document.

1. Introduction

This section is not normative.

Currently, web applications are almost always compartmentalized by using separate host names to establish separate web origins. This is useful for helping to prevent XSS and other cross-origin attacks, but has many unintended consequences. For example, it causes latency due to additional DNS lookups, removes the ability to use single-origin features (such as the history.pushState API), and creates cryptic host name changes in the user experience. Perhaps most importantly, it results in an extremely inflexible architecture that, once rolled out, cannot be easily and transparently changed later on.

There are several mechanisms for reducing the attack surface for XSS without creating separate host-name based origins, but each pose their own problems. Per-page Suborigins is an attempt to fill some of those gaps. Two of the most notable mechanisms are Sandboxed IFrames [IFrameSandbox] and Content Security Policy (CSP) [CSP2]. Both are powerful but have shortcomings and there are external developers building legacy applications that find they cannot use those tools.

Application developers can use sandboxed frames to completely isolate untrusted content, but there are a number of problems with such an approach. Since user agents assign sandboxed frames a random, unpredictable origin, it is very difficult, by design, for them to communicate with other frames or use modern, origin-bound mechanisms like postMessage and CORS. Further, there is no easy way to persist client-side state (e.g., using localStorage or sessionStorage) in random, unpredictable origins. Finally, sandboxed frames not only sandbox the application but also any other frames loaded by the appplication. This makes this technique infeasible for any page relying on third-party integrations. Moreover, because they are by definition unique origins, with no relationship to the original origin, designing permissions for them to access resources of the original origin would be difficult.

Content Security Policy is also promising but is generally incompatible with current website design. Developers and security teams have found it impractical to retrofit legacy applications with it. On top of this, until all applications hosted within a single origin are simultaneously put behind CSP, the mechanism offers limited incremental benefits, which is especially problematic for companies with large portfolios of disparate products all under the same domain.

1.1. Goals

1.2. Use Cases/Examples

We see effectively three different use cases for Per-page Suborigins:

  1. Separating distinct applications served from the same domain due to deployment issues but do not need to extensively interact with other content. Examples include marketing campaigns, simple search UIs, and so on.

  2. Enable secure isolation at modular boundaries within a larger web application by splitting the functional components into different suborigins. For example, a blogging application might isolate the main blog viewership module, the admin module, and the authoring module via separate suborigins.

  3. Similar to (2), applications with many users can split information relating to different users into their own suborigin. For example, a social network might put each user profile into a unique suborigin so that an XSS within one profile cannot be used to immediately infect other users or read their personal messages stored within the account.

https://example.com/ runs two applications, Chat and Shopping, used, respectively, for instant messaging and Internet shopping. The site adminstrator runs the former at https://example.com/chat/, and the latter at https://example.com/shopping/.

The Shopping application has been very well tested and generally does not contain much untrusted content. In fact, it only takes simple text from advertisers, and that text only ever appears in HTML contexts, so the application is able to entity encode the text and stop nearly all cross-site scripting attacks on the application. Further, the developers have also enabled a strong Content Security Policy to mitigate potential XSS concerns on all pages under https://example.com/shopping/. The CSP policy only allows scripts from scripts.example.com.

Historically, https://example.com/chat/ has been riddled with cross-site scripting attacks. The application takes untrusted content from a wider variety of sources and for added complexity, that content ends up in many more contexts, such as HTML tag attributes. On top of that, the developers never bothered creating a CSP for the application.

This is bad enough, but, unfortunately, it has led to the extremely bad consequence of attackers using the low hanging fruit of Chat to attack Shopping, the more desirable target. Cross-site scripting Shopping allows an attacker to buy goods with the user’s account, so this is really the juicy target.

Since the applications share the same physical origin, these attacks have not traditionally been that difficult. Once an attacker has executed code on Chat with an XSS, they open a new window or iframe at example.com/shopping/. Since this is at the same origin as Chat, this allows the attacker to inject code through the document object of the window or iframe into the Shopping context, allowing the attacker to buy whatever they’d like.

Historical and branding reasons require hosting both applications on the example.com origin. Thus, while these two applications are completely separate, the company cannot split the products into two different origins (e.g. examplechat.com and exampleshopping.com) or different suborigins (e.g. chat.example.com and shopping.example.com).

To address this, the developers decide to serve both applications on two separate suborigins. For all HTTP requests to any subpath of /chat or /shopping, example.com includes a header suborigin: chat or suborigin: shopping, respectively.

This does not remove any of the XSS attacks on Chat. However, when an attacker injects code into Chat and opens a window or iframe to example.com/shopping/, they can no longer inject content through the document as it will fail the same origin check. Of course, the application can still use XMLHttpRequest and postMessage to communicate with the document, but that will only be through well defined APIs. In short, the CSP of the Shopping application is now actually effective as the permissive Chat application is no longer a bypass of it.

TODO: We probably should add additional examples, or perhaps match an example to each bullet above.

2. Key Concepts and Terminology

TODO(jww) This needs to be filled in once we have a pretty good handle on the basic structure of this document. At that point, we should extract the terms defined throughout the spec and place them here.

This section defines several terms used throughout the document.

CORS, or Cross-Origin Resource Sharing, are defined by the CORS specification. [CORS]

XMLHttpRequest, or XHR, is defined by the XMLHttpRequest specification. [XHR]

The term cross-site scripting, or XSS for short, refers to a content injection attack where an attacker is able to execute malicious code in a victim origin. See the OWASP page on Cross-site Scripting for more information.

2.1. Grammatical Concepts

The Augmented Backus-Naur Form (ABNF) notation used in this document is specified in RFC5234. [RFC5234]

Lowercase characters, the a-z portion of ALPHA, are defined by the grammar:

LOWERALPHA = %x61-7A   ; a-z

3. Defining a Suborigin

Origins are a mechanism for user agents to group URIs into protection domains. Two URIs are in the same-origin if they share the same scheme, host, and port. If URIs are same-origin, then they share the same authority and can access all of each others resources.

Compared to per-user isolation in traditional operating systems, the same-origin policy isolates distinct applications identified by their origins. This has been a successful isolation mechanism on the Web. However, it does limit the flexibility of a page to separate itself into a new protection domain as it automatically shares authority with all other identical origins. These origins are defined by physical, rather than programmatic, properties. While it is possible to setup unique domains and ports for different parts of the same application (scheme is more difficult to separate out), there are a diverse set of practical problems in doing so.

Suborigins provide a mechanism for creating this type of separation programatically. Any resources may provide, in a manner detailed below, a string value suborigin namespace. If either of two URIs provide a suborigin namespace, then the two URIs are in the same-origin if and only if they share the same scheme, host, port, and suborigin namespace.

3.1. Difficulties using subdomains

This section is not normative.

At first glance, subdomains provide an ability to setup multiple origins and isolate applications. Unfortunately, a number of practical difficulties make relying on subdomains infeasible.

3.1.1. Separate applications, same origin

Google runs Search and Maps on the same domain, respectively https://www.google.com and https://www.google.com/maps. While these two applications are fundamentally separate, there are many reasons for hosting them on the same origin, including historical links, branding, and performance. However, from security perspective, this means that a compromise of one application is a compromise of the other since the only security boundary in the browser is the origin, and both applications run in the same origin. Thus, even if Google Search were to successful implement a strong Content Security Policy [CSP2], if Google Maps were to have an XSS vulnerability, it would be equivalent to having an XSS on Google Search as well, negating Google Search’s security measures.

3.1.2. Separation within a single application

Separation is sometimes desirable within a single application because of the presence of untrusted data. Take, for example, a social networking site with many different user profiles. Each profile contains lots of untrusted content created by a single user but it’s all hosted on a single origin. In order to separate untrusted content, the application might want a way to put all profile information into separate logical origins while all being hosted at the same physical origin. Furthermore, all content within a profile should be able to access all other content within the same origin, even if displayed in unique frames.

This type of privilege separation within an application has been shown to be valuable and reasonable for applications to do by work such as Privilege Separation in HTML5 Applications by Akhawe et al [PRIVILEGESEPARATION]. However, these systems rely on cross frame messaging using postMessage even for content in the same trust boundary since they utilize sandbox. This provides much of the motivation for the named container nature of suborigins.

3.2. Threat Model

Origins and the Same-Origin Policy have provided a strong defense against malicious applications. Instead of giving the application the power of the user, applications on the Web are limited to a unique space that is defined by their host. However, by tying the origin to the physical host, this has limited the power of developers.

Suborigins attempt to provide developers with tool to contain two different principles that are on the same host. Suborigins allow two or more applications or modules to be hosted at the same origin but use the same origin policy to separate them from each other.

3.2.1. Cross-Document Attacker

An attacker that is able to compromise one document should not be able to control another document that is on the same host but delivered in a different suborigin namespace. If an attacker is able to XSS, for example, a document on example.com delivered in the suborigin namespace foo, the attacker should not be able to control any document on example.com not in the foo namespace.

TODO(devd): Should we also assert that attacker on main/parent origin cannot compromise the app in sub-origin?

"Should not control" is a stronger statement than I think we want to make. The browser can only isolate per origin isolation. If the app has a bug (due to postmessage or server-side) the browser can’t do anything. We should stick to saying "are isolated like physical origins."

3.2.2. Out of Scope Attacker

This tool is purely for modularity and meant to be an application security tool. It is not meant to help users differentiate between two different applications at the same host, as reflected by the fact that user agents may not put the suborigin in user-visible UI. Additionally, suborigins cannot protect against colluding malicious or compromised applications.

3.3. Relationship of Suborigins to Origins

Suborigins, in fact, do not provide any new authority to resources. Suborigins simply provide an additional way to construct Origins. That is, Suborigins do not supercede Origins or provide any additional authority above Origins. From the user agent’s perspective, two resources in different Suborigins are simply in different Origins, and the relationship between the two resources should be the same as any other two differing origins as described in HTML Standard §origin and [RFC6454]. However, given the impracticalities this may impart on some applications who might want to adopt Suborigins, a few security-model opt-outs to ease the use of Suborigins in legacy applications are also presented. See §6.3 Security Model Opt-Outs for more information.

3.4. Opting into a Suborigin

Unlike the sandbox attribute, suborigin namespaces are predictable and controllable. Because of this, potentially untrusted content cannot opt into suborigins, unlike iframe sandboxes. If they could, then an XSS on a site could enter a specific suborigin and access all of its resources, thus violating the isolation suborigins intend to provide. To prevent this, the server (rather than a resource itself) is the only authoritative source of the suborigin namespace of a resource. The server communicates the suborigin of a resource to the user agent through a new suborigin header, which takes a string value that is the namespace. For example, to put a resource in the testing suborigin namespace, the server would specify the following HTTP header in the response:

suborigin: testing

3.5. The suborigin header

Suborigins are defined by a suborigin HTTP response header. The syntax for the name and value of the header are described by the following ABNF grammar [RFC5234]:

suborigin-name = LOWERALPHA *( LOWERALPHA / DIGIT )
suborigin-policy-option = "'unsafe-postmessage-send'"
                          / "'unsafe-postmessage-receive'"
                          / "'unsafe-cookies'"
                          / "'unsafe-credentials'"
suborigin-policy-list = 1*(RWS suborigin-policy-option OWS)
suborigin-header = suborigin-name [ suborigin-policy-list ]

User agents MUST ignore multiple suborigin headers and only apply the first.

A resource’s suborigin namespace is the value of the suborigin-name in the suborigin header.

A resource’s suborigin policy is the list of individual suborigin-policy-option values in the suborigin header’s suborigin-policy-list.

Note: A suborigin name must start with a lowercase character, but after the first character, the name may contain lowercase characters or numerals. This is to avoid potential confusion when the origin is serialized if the serialization started with a number.

3.6. Representation of Suborigins

At an abstract level, a suborigin consists of the physical origin, which is a scheme, host, and port, plus a suborigin namespace. However, as mentioned above, suborigins are intended to fit within the framework of HTML Standard §origin and [RFC6454]. Therefore, this specification provides a way of serializing a Suborigin bound resource into a physical origin. This is done by inserting the suborigin namespace into the host of the Origin, thus creating a new host but maintaining all of the information about both the original scheme, host, port, and the suborigin namespace. The serialization format appends the string "-so" to the scheme and prepends the host name with the suborigin namespace followed by a "." character.

For example, a resource hosted at https://example.com/ in the suborigin namespace profile would be serialized as https-so://profile.example.com/.

Similarly, a resource hosted at https://example.com:8080/ in the suborigin namespace separate would be serialized as https-so://separate.example.com:8080/.

Internally, the user agent just tracks the suborigin namespace of the resource. When the origin needs to be serialized, the user agent should follow the algorithm in §6.1.6 Serializing Suborigins.

TODO: Determine how the serialization should relate to the URL spec: https://url.spec.whatwg.org/#host-parsing

3.7. Accessing the Suborigin in JavaScript

A suborigin property is added to the document object which reflects the value of the suborigin namespace for the current execution context. If there is no suborigin namespace, the value should be undefined.

Additionally, the origin property of the document object should reflect the serialized value of the origin as returned by §6.1.6 Serializing Suborigins.

TODO(jww): Need to write the formal IDL for this.

4. Access Control

Cross-origin (including cross-suborigin) communication is tricky when suborigins are involved because they need to be backwards compatible with user agents that do not support suborigins while providing origin-separation for user agents that do support suborigins. The following discussions discuss the three major cross-origin mechanisms that are relevant: CORS, postMessage, and workers [WORKERS].

TODO(devd): Making things specific to XHR or CORS is weird. We should just make all fetches inside a suborigin CORS fetches and be done with it.

4.1. CORS

For pages in a suborigin namespace, all XMLHttpRequests and fetch requests to any URL should be treated as cross-origin, thus triggering a cross-origin request with preflight for all non-simple cross-origin requests. Additionally, all requests from a suborigin namespace must include a Suborigin header whose value is the context’s suborigin name. Finally, the Origin header [FETCH] value must use the serialized suborigin value instead of the serialized origin, as described in §6.1.6 Serializing Suborigins.

Similar changes are needed for responses from the server with the addition of an Access-Control-Allow-Suborigin response header. Its value must match the context’s suborigin namespace value, or * to allow all suborigin namespaces. At the same time, the Access-Control-Allow-Origin response header value must be modified to use the serialized suborigin value instead of the serializied origin, as described in §6.1.6 Serializing Suborigins.

Note: Since the suborigin of a server resource is unknown until fetched, all requests, even to resources in the same suborigin are cross-origin. Concretely, imagine putting https://example.com/widget/ in the 'widget' suborigin. An XmlHttpRequest from /widget/index.html to /widget/data.json will be treatd as a cross origin request. A simple XmlHttpRequest will need an Access-Control-Allow-Suborigin header allowing the page to read the response. An XmlHttpRequest of content-type text/json will be preflighted.

TODO(jww): Formal definition of the headers and responses w/grammars. Also need to be explicit about * having same limitations as Access-Control-Allow-Origin w/credentials.

Access-Control-Allow-Origin * fails with credentialed requests. Does Access-Control-Allow-Suborigin have the same behavior? We should detail that in more detail.

Browsers have a really low cache time (15mins) for preflight responses which makes CORS a big pain for websites. This will become even more salient with suborigins. Should the spec ask browsers to increase cache times?

Serializing the suborigin value into the origin header seems like it will break things. Whats the risk with serializing the physical origin in the origin header? The browser won’t let you read the responses (or succeed preflight) if neither of them have the right allow-suborigin behavior. This seems more compatible with no security risk.

4.2. postMessage

Cross-origin messaging via postMessage requires that the recipient be able to see the suborigin namespace of the message sender so it can make an appropriate access control decision. We introduce a new dictionary ExtendedOrigin that holds information about both the origin and the suborigin namespace:

dictionary ExtendedOrigin {
  USVString origin = "";
  USVString? suborigin = null;
};

We’re monkey-patching HTML, submit a PR when we’re sure this is the general direction we want to go in.

And we extend the definition of the MessageEvent and MessageEventInit as follows:

interface MessageEvent {
  readonly attribute USVString? origin;
  readonly attribute ExtendedOrigin extendedOrigin;
  void initMessageEvent(DOMString type, optional boolean bubbles = false, optional boolean cancelable = false, optional any data = null, optional ExtendedOrigin? extendedOrigin = null, optional DOMString lastEventId = "", optional MessageEventSource? source = null, optional sequence<MessagePort> ports = []);
};

dictionary MessageEventInit {
  ExtendedOrigin? extendedOrigin = null;
};

Similarly, we add a new overloaded version of postMessage to the Window interface:

partial interface Window {
  void postMessage(any message, optional ExtendedOrigin targetOrigin, optional sequence<object> transfer = []);
};

Those new interfaces can also be used to communicate with browsing contexts that aren’t in a suborigin namespace. Furthermore, the origin attribute of an MessageEvent will be null (the value, not the string) if the suborigin attribute of the event’s extendedOrigin is non-null, unless either unsafe-postmessage-receive or unsafe-postmessage-send was specified (depending on whether the context inside the suborigin, or its communication peers don’t support this API).

If the suborigin attribute of the EventInitializer passed to postMessage is the literal string *, it will match any suborigin namespace, including if the receiver doesn’t have any suborigin. Otherwise, the suborigin has to match exactly. A browsing context that doesn’t specify a suborigin is matched by the value null, and not by the empty string. Again, the restriction that a suborigin namespace is required can be removed via the unsafe-postmessage-receive and unsafe-postmessage-send options.

Website authors can use the presence of the EventInitializer constructor to feature detect the presence of these new interfaces. For postMessage and initMessageEvent, authors can also add a custom toString method to the dictionary they pass to it like so:

postMessage(data, {
    'origin': 'http://example.com',
    'suborigin': '*',
    'toString': () => { return 'http://example.com' }
});

TODO(jww): Formalize by updating the postMessage algorithm

4.3. Workers

User agents MUST refuse to create or execute any workers in a page executing in a sub-origin.

TODO: Formalize. Not sure what this will look like yet. Chrome and Opera don’t allow creation of workers from sandbox’d iframes/the 'null' origin, but not sure if this formalized in a spec. If it is, should probably just update whatever disallows creation from sandbox.

Note: This may change in the future, and suborigins may eventually be allowed to register service workers, but, for now, allowing the creation of any workers, including service workers and shared workers, from suborigins adds too many complications. Applications can still create workers by creating an iframe of a page not in a suborigin.

5. Impact on Web Platform

Content inside a suborigin namespace is restricted in the same way that other origins are restricted. There are some additional restrictions as well, in order to simplify some complicated cases.

5.1. Storage

The storage APIs, such as localStorage and sessionStorage, are accessible from within suborigins. By nature of their APIs, they are bound to the origin of the Document, which has the practical effect of giving a separate Storage object to each suborigin namespace. Per the definitions in localStorage and sessionStorage, the user agent MUST provide separate Storage objects per origin, and thus per suborigin namespace.

5.2. document.domain

The document.domain property User agents MUST ignore modifications to the document.domain property of the page.

5.3. WebSockets

The WebSocket() constructor algrithm [WebSockets] is modified as follows:

After the current step 1, perform the following step:

  1. If the origin property of the client’s relevant settings object has a suborigin, throw a SecurityError exception.

Note: Suborigins are likely to allow WebSockets in the future, but are disabled until it can be decided how they should be protected.

6. Framework

Note: These sections are tricky because, unlike physical origins, we can’t define suborigins in terms of URIs. Since the suborigin namespace is defined in a header, not in the URI, we need to define them in terms of resources.

6.1. Updates to Origin

Suborigins extends the origin concept. The following sections define how this extension works.

6.1.1. Suborigin of a Response

TODO: This needs update Fetch to change the origin of response. Maybe just need to make sure that response’s URL has the suborigin serialized?a https://fetch.spec.whatwg.org/#responses

6.1.2. Origin Tuple

Update the definition of tuple origin in the origin section of [HTML] to read:

A tuple consists of:

6.1.3. Physical Origin

The physical origin of an origin is an origin tuple where the components are:

6.1.4. Comparing Origins

Update the same origin algorithm in [HTML] such that step 2 reads:

  1. If A and B are both tuple origins and their schemes, hosts, ports, and suborigins are identical, then return true.

6.1.5. Comparing Physical Origins

Two origins, A and B, are said to be same physical origin if the following algorithm returns true:

  1. Let AP be the physical origin of A. Let BP be the physical origin of B.

  2. If AP and BP are the same origin, return true.

  3. Return false.

6.1.6. Serializing Suborigins

This section defines how to serialize an origin to a Unicode [Unicode6] string and to an ASCII [RFC0020] string.

6.1.6.1. Unicode Serialization of a Suborigin

Update the Unicode serialization of an origin in [HTML] by modifying step 4 to read:

  1. Let unicodeOrigin be a new tuple origin consisting origin’s scheme, unicodeHost, origin’s port, and origin’s suborigin.

6.1.6.2. ASCII Serialization of a Suborigin

Update the steps of ASCII serialization of an origin in [HTML] to read:

  1. If origin is an opaque origin, then return "null".

  2. Otherwise, let result be origin’s scheme.

  3. Let schemeSuffix be "-so://" if the suborigin component of origin is not the empty string, and "://" otherwise.

  4. Let hostPrefix be the suborigin component of origin with "." appended to it if the suborigin component of origin is not the empty string, and "" otherwise.

  5. Append schemeSuffix to result.

  6. Append hostPrefix to result.

  7. Append origin’s host, serialized, to result.

  8. If origin’s port is non-null, append a U+003A COLON character (:), and origin’s port, serialized, to result.

  9. Return result.

6.1.6.3. Document object’s origin

TODO: Need to assign how Document gets a suborigin. Need to patch the "For Document objects" subsection of https://html.spec.whatwg.org/#origin. Will also probably need to update https://fetch.spec.whatwg.org/#concept-response to have a suborigin property, which uses that response to assign a suborigin to the origin tuple for Object, similar to https://w3c.github.io/webappsec-csp/#sandbox-init.

6.2. Interactions with the DOM

6.2.1. Cookies

Append the following to the list of conditions of Document objects that are cookie-averse in Section 3.1.2 of HTML5’s resource metadata management:

Modify the paragraph following this list to read "scheme/host/port/suborigin tuple" instead of "scheme/host/port tuple".

Additionally, modify step 1 of Cookie setter to read:

  1. If the meta element has no content attribute, or if that attribute’s value is the empty string, or if the Document is cookie-averse then abort these steps.

Note: A cookie-averse Document object has the property that direct access to document.cookie returns the empty string, and assigning to document.cookie has no effect whatsoever. However, that network cookies are not affected and documents with different suborigin namespaces on the same physical origin share the same cookies on the network.

Note: For practical purposes, this means that a developer cannot use document.cookie directly because assignment and reading of the object are both no-ops. However, a cookie-averse Document may still use getters and setters on the cookie property of the document object and, in that way, may still simulate cookie access.

Currently, some browsers do not let applications redefine getters and setters on cookie averse documents. Should the spec enforce that this is allowed?

6.3. Security Model Opt-Outs

For backwards compatibility, Suborigins provide several opt-opts from the standard security model. A developer can choose to use these opt-outs by specifying a suborigin policy in the suborigin header

Since these opt-outs weaken the security model of suborigins, developers SHOULD NOT use these options unless they are required to make their application work.

The values of suborigin-policy-option that may be present in a suborigin policy have the following effects:

6.3.1. 'unsafe-postmessage-receive'

If unsafe-postmessage-receive is set, MessageEvents sent to browsing context in the respective suborigin namespace will expose the serialization of their physical origin via the origin attribute of the MessageEvents. Browsing context sending MessageEvents to the suborigin do not need to specify a target suborigin.

6.3.2. 'unsafe-postmessage-send'

If unsafe-postmessage-send is set, the legacy postMessage method can be used to communicate from within the browsing context in the respective suborigin. The MessageEvents dispatched in response to a postMessage from the respective suborigin will expose the serializatoin of the suborigin’s physical origin via the origin attribute.

6.3.3. 'unsafe-cookies'

Normally, a Document with a non-empty suborigin namespace is cookie-averse, which means that cookies cannot be read or written. However, if the suborigin policy contains the unsafe-cookies option, the Document is not made cookie-averse, which leaves cookies readable and writable by the execution context. See §6.2.1 Cookies for the precise definition of how this is defined.

Practically speaking, this means that code executing in this suborigin can access and set cookies for any other suborigin at that physical origin, including the empty suborigin. Thus, this option SHOULD NOT be used if cookies are security-sensitive in your application.

Note: This option is intended only for easing deployment. Application authors should carefully evaluate the risk and impact on their application before turning it on. The untrusted sub-origin code can overwrite session cookies and execute a session fixation attack on the applications running in the physical origin. Worse, if an application on the physical origin relies on simple double-submit CSRF tokens (not tied to the session), then an untrusted sub-origin can execute arbitrary CSRF attacks. Similarly, if session ccookies or CSRF cookies are readable from javascript (i.e., they are not Http-Only), then the untrusted sub-origin can steal the user’s session or execute arbitrary CSRF attacks.

6.3.4. 'unsafe-credentials'

All cross-origin requests to the physical origin for the execution context will include credentials if the 'unsafe-credentials' suborigin policy option for the execution context is set.

Specifically, update the step 2 of HTTP-network-or-cache fetch in the Fetch spec [FETCH] to read:

  1. Let credentials flag be set if one of

Note: This has several important, practical effects. If the credentials mode of a fetch() is set to "same-origin", and if it is to the same physical origin as the current execution context, credentials will be included. This means that if the crossorigin attribute on elements such as <img> are set to anonymous, credentials will be sent if the origin of the URL is the same physical origin. Similarly, if making an XMLHttpRequest, setting this flag is equivalent to setting withCredentials to true for requests to the same physical origin. This means that it credentials will be sent even for requests to another suborigin at the same physical origin.

TODO(jww,aaj): Provide an example of why this might be needed.

TODO: These opt-out descriptions should probably be moved to the individual sections where the behaviors are discussed (i.e. postMessage and cookies). These just need to be fleshed out anyway, and examples and reasons need to be given. Also, they need to update the processing model, such as adding appropriate flags to the environment settings object.

7. Practical Considerations in Using Suborigins

Using suborigins with a Web application should be relatively simple. At the most basic level, if you have an application hosted on https://example.com/app/, and all of its resources are hosted at subpaths of /app, it requires that the server set a Content Security Policy on all HTTP requests to subpaths of /app that contain the header suborigin: namespace, where namespace is of the application’s choosing. This will ensure that the user agent loads all of these resources into the suborigin namespace and will enforce this boundary accordingly.

Additionally, if your application allows cross-origin requests, instead of adding the usual Access-Control-Allow-Origin header for cross-origin requests, add Access-Control-Allow-Suborigin headers, as defined in §4.1 CORS.

In the client-side portion of the application, if postMessage is used, the application must be modified so it does not check the event.origin field. Instead, it the event.suborigin fields, as they are defined in §4.2 postMessage.

TODO(devd): Flesh out the above and make sure it covers all it needs to cover.

8. Security Considerations

8.1. Presentation of Suborigins to Users

A complication of suborigins is that while they provide a meaningful security for an application, that boundary makes much less sense to a user. That is, physical origins provide a security boundary at a physical level: separate scheme, hosts, and ports map to real boundaries external of a given application. However, suborigins as a boundary only makes sense within the context of the program logic itself, and there is no meaningful way for users to make decisions based on suborigins a priori.

Therefore, suborigins should be used only internally in a user agent and MUST NOT be presented to users at all. For example, user agents must never present suborigins in link text or a URL bar.

Is history.pushState allowed in a suborigin? I suggest no to reduce complexity and inherit from sandbox behavior. On the other hand, this impacts compatibility.

8.2. Not Overthrowing Same-Origin Policy

Suborigins do not fundamentally change how the same-origin policy works. An application without suborigins should work identically to how it always has, and even in an application with suborigins, the same-origin policy still applies as always. In fact, this document defines suborigins within the context of the same-origin policy so that, in theory, serialized suborigins can be thought of as a just a special case of the traditional same-origin policy.

8.3. Problems with Serialized Representation

TODO: Need to list concerns with serialization, e.g. apps that don’t recognize the suborigin serialization, esp. if they blocklist origins.

9. Privacy Considerations

TODO: Do we have privacy issues?

Conformance

Document conventions

Conformance requirements are expressed with a combination of descriptive assertions and RFC 2119 terminology. The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, and “OPTIONAL” in the normative parts of this document are to be interpreted as described in RFC 2119. However, for readability, these words do not appear in all uppercase letters in this specification.

All of the text of this specification is normative except sections explicitly marked as non-normative, examples, and notes. [RFC2119]

Examples in this specification are introduced with the words “for example” or are set apart from the normative text with class="example", like this:

This is an example of an informative example.

Informative notes begin with the word “Note” and are set apart from the normative text with class="note", like this:

Note, this is an informative note.

Conformant Algorithms

Requirements phrased in the imperative as part of algorithms (such as "strip any leading space characters" or "return false and abort these steps") are to be interpreted with the meaning of the key word ("must", "should", "may", etc) used in introducing the algorithm.

Conformance requirements phrased as algorithms or specific steps can be implemented in any manner, so long as the end result is equivalent. In particular, the algorithms defined in this specification are intended to be easy to understand and are not intended to be performant. Implementers are encouraged to optimize.

Index

Terms defined by this specification

Terms defined by reference

References

Normative References

[CORS]
Anne van Kesteren. Cross-Origin Resource Sharing. 16 January 2014. REC. URL: https://www.w3.org/TR/cors/
[DOM]
Anne van Kesteren. DOM Standard. Living Standard. URL: https://dom.spec.whatwg.org/
[FETCH]
Anne van Kesteren. Fetch Standard. Living Standard. URL: https://fetch.spec.whatwg.org/
[HTML]
Anne van Kesteren; et al. HTML Standard. Living Standard. URL: https://html.spec.whatwg.org/multipage/
[HTML51]
Steve Faulkner; et al. HTML 5.1. URL: https://www.w3.org/TR/html51/
[RFC0020]
V.G. Cerf. ASCII format for network interchange. October 1969. Internet Standard. URL: https://tools.ietf.org/html/rfc20
[RFC2119]
S. Bradner. Key words for use in RFCs to Indicate Requirement Levels. March 1997. Best Current Practice. URL: https://tools.ietf.org/html/rfc2119
[RFC5234]
D. Crocker, Ed.; P. Overell. Augmented BNF for Syntax Specifications: ABNF. January 2008. Internet Standard. URL: https://tools.ietf.org/html/rfc5234
[Unicode6]
The Unicode Consortium. The Unicode Standard, Version 6.2.0. URL: http://www.unicode.org/versions/Unicode6.2.0/
[URL]
Anne van Kesteren. URL Standard. Living Standard. URL: https://url.spec.whatwg.org/
[WebIDL]
Cameron McCormack; Boris Zbarsky; Tobie Langel. Web IDL. URL: https://heycam.github.io/webidl/
[WebSockets]
Ian Hickson. The WebSocket API. 20 September 2012. CR. URL: https://www.w3.org/TR/websockets/
[WORKERS]
Ian Hickson. Web Workers. URL: https://www.w3.org/TR/workers/
[XHR]
Anne van Kesteren. XMLHttpRequest Standard. Living Standard. URL: https://xhr.spec.whatwg.org/

Informative References

[CSP2]
Mike West; Adam Barth; Daniel Veditz. Content Security Policy Level 2. URL: https://www.w3.org/TR/CSP2/
[IFrameSandbox]
Mike West. Play safely in sandboxed IFrames. URL: http://www.html5rocks.com/en/tutorials/security/sandboxed-iframes/
[PRIVILEGESEPARATION]
Devdatta Akhawe; Prateek Saxena; Dawn Song. Privilege Separation in HTML5 Applications. URL: https://www.usenix.org/system/files/conference/usenixsecurity12/sec12-final168.pdf
[RFC6454]
A. Barth. The Web Origin Concept. December 2011. Proposed Standard. URL: https://tools.ietf.org/html/rfc6454

IDL Index

dictionary ExtendedOrigin {
  USVString origin = "";
  USVString? suborigin = null;
};

interface MessageEvent {
  readonly attribute USVString? origin;
  readonly attribute ExtendedOrigin extendedOrigin;
  void initMessageEvent(DOMString type, optional boolean bubbles = false, optional boolean cancelable = false, optional any data = null, optional ExtendedOrigin? extendedOrigin = null, optional DOMString lastEventId = "", optional MessageEventSource? source = null, optional sequence<MessagePort> ports = []);
};

dictionary MessageEventInit {
  ExtendedOrigin? extendedOrigin = null;
};

partial interface Window {
  void postMessage(any message, optional ExtendedOrigin targetOrigin, optional sequence<object> transfer = []);
};

Issues Index

TODO: We probably should add additional examples, or perhaps match an example to each bullet above.
TODO(jww) This needs to be filled in once we have a pretty good handle on the basic structure of this document. At that point, we should extract the terms defined throughout the spec and place them here.
TODO(devd): Should we also assert that attacker on main/parent origin cannot compromise the app in sub-origin?
"Should not control" is a stronger statement than I think we want to make. The browser can only isolate per origin isolation. If the app has a bug (due to postmessage or server-side) the browser can’t do anything. We should stick to saying "are isolated like physical origins."
TODO: Determine how the serialization should relate to the URL spec: https://url.spec.whatwg.org/#host-parsing
TODO(jww): Need to write the formal IDL for this.
TODO(devd): Making things specific to XHR or CORS is weird. We should just make all fetches inside a suborigin CORS fetches and be done with it.
TODO(jww): Formal definition of the headers and responses w/grammars. Also need to be explicit about * having same limitations as Access-Control-Allow-Origin w/credentials.
Access-Control-Allow-Origin * fails with credentialed requests. Does Access-Control-Allow-Suborigin have the same behavior? We should detail that in more detail.
Browsers have a really low cache time (15mins) for preflight responses which makes CORS a big pain for websites. This will become even more salient with suborigins. Should the spec ask browsers to increase cache times?
Serializing the suborigin value into the origin header seems like it will break things. Whats the risk with serializing the physical origin in the origin header? The browser won’t let you read the responses (or succeed preflight) if neither of them have the right allow-suborigin behavior. This seems more compatible with no security risk.
We’re monkey-patching HTML, submit a PR when we’re sure this is the general direction we want to go in.
TODO(jww): Formalize by updating the postMessage algorithm
TODO: Formalize. Not sure what this will look like yet. Chrome and Opera don’t allow creation of workers from sandbox’d iframes/the 'null' origin, but not sure if this formalized in a spec. If it is, should probably just update whatever disallows creation from sandbox.
Currently, some browsers do not let applications redefine getters and setters on cookie averse documents. Should the spec enforce that this is allowed?
TODO(jww,aaj): Provide an example of why this might be needed.
TODO: These opt-out descriptions should probably be moved to the individual sections where the behaviors are discussed (i.e. postMessage and cookies). These just need to be fleshed out anyway, and examples and reasons need to be given. Also, they need to update the processing model, such as adding appropriate flags to the environment settings object.
TODO(devd): Flesh out the above and make sure it covers all it needs to cover.
Is history.pushState allowed in a suborigin? I suggest no to reduce complexity and inherit from sandbox behavior. On the other hand, this impacts compatibility.
TODO: Need to list concerns with serialization, e.g. apps that don’t recognize the suborigin serialization, esp. if they blocklist origins.
TODO: Do we have privacy issues?