Friday, August 2, 2013

Suborigins for Privilege Separation in Web Applications

One area of browser development that I have wanted to work on for a while now is privilege separation in web applications. In particular, I've always wanted a mechanism to help developers write truly modular applications, where the different parts are strictly separated by the browser. There are some very cool tools an ideas for how to do this within an application itself (my personal favorites being in Devdatta Akhawe's work on data confinement and least privilege in the web applications), but I would feel a heck of a lot better with a browser mechanism for this.

What are some examples of this? Well, often, there are many web applications that share a single real origin. Perhaps the most obvious example of this is my employer, Google, whose main origin, www.google.com hosts many functionally separate applications (in the hundreds), but share an origin for reasons relating to branding, DNS latency, and seamless interoperability, despite being very different properties. This has an unfortunate side effect, though, that one compromised web application from an XSS at the www.google.com origin means that all properties at www.google.com are compromised, and nobody wants that. There are a lot of popular web applications that face similar dilemmas, such as Facebook, Twitter, Dropbox, etc.

Browsers have several mechanisms these days that start to address this compartmentalization problem, but each has its own problem. Sandboxed iframes are a mechanism for containing totally untrusted data, but their synthetic origins make them, by design, very difficult to communicate with. Content Security Policy (CSP), which I'm a big fan of, is a great system for eliminating cross-site scripting (XSS) attacks (among other things), but is generally incompatible with current website design. Additionally, using CSP requires complete compliance across the entire origin, so if you have a lot of web applications at the same origin, and any one of them does not use CSP, you're entire origin is at risk. We really want something that would allow us to, among other things, adopt CSP piecemeal, one web application at a time, despite remaining at the same physical origin.

In my first several weeks at Google, I spoke to Michal Zalewski and Adam Barth about an idea of Michal's to provide exactly this kind of separation. We're calling this idea Per-page Suborigins, and you can see a detailed proposal here. Our objective is to provide a new mechanism for allowing sites to easily separate their content into separate, flexible synthetic origins, that are transparent to users, while still serving content from a single physical origin. Furthermore, the synthetic origins should be predictable and convey the full physical origin so that compartmentalized content can easily use current browser technologies, such as postMessage, to interact with each other.
I'll elide most of the details in this post and leave them them for you to explore in the aforementioned detailed proposal. I really wanted to throw this idea out to the public as soon as possible to get as many ideas and as much feedback as possible, so I'll just limit myself to a basic overview in this post.

Overview

As mentioned earlier, many web applications share a single real origin for a variety of reasons, most of them pragmatic. In a sense, this is an accidental byproduct of the Same Origin Policy (SOP) in the sense that the SOP assumes everything at a given real origin is part of the same application. So our goal is to create a primitive that would allow developers to apply the SOP at a finer grained granularity and specify that different applications within the same real origin should, in fact, be treated as different origins under the SOP. This would fill a space between Sandboxed Frames and CSP by allowing consumers to separate trusted components into separated origins while still allowing efficient cross-origin communication via postMessage and CORS, but also without significant retrofitting limitations to legacy applications.

In terms of actual use, there are three different use cases that we are aiming for:
  1. Separating distinct applications that happen to be served from the same domain, but do not need to extensively interact with other content. Examples include marketing campaigns, simple search UIs, and so on. This use requires very little engineering effort and faces very few constraints; the applications may use XMLHttpRequest and postMessage to communicate with their host domain as required.
  2. Allowing for modularity within a larger web application by splitting the functional components into different suborigins. For example, Gmail might put the contacts widget, settings tab, and HTML message views in separate Per-page Suborigins. Such deployments may require relatively modest refactorings to switch to postMessage and CORS where direct DOM access and same-origin XMLHttpRequest are currently used, but we believe doing so is considerably easier than retrofitting CSP onto arbitrary code bases and can be done very incrementally.
  3. Similar to (2), applications with many users can split information relating to different users into their own suborigin. For example, Twitter 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.
Our proposal for Per-page Suborigns is an attempt to address all three of these uses. It allows servers to provide the browser in an HTTP response with a suborigin name. The browser then treats this as another part of the origin in SOP checks. That is, instead of just comparing scheme, host, and port, the browser will also verify that the two origins have the same suborigin name. Because these suborigins can only be created by the server in a new execution context (i.e. a new frame), in some ways you can think of this as a named Sandboxed Frame.

(Some) Details

Before we get any further, let's define a suborigin and how it interacts with the SOP. The good news is, it's pretty straightforward. A suborigin is a synthetic origin defined by an HTTP header such as:
   Suborigin: <name>
An alternative to this that has some nice properties is to define it as a CSP directive. For example:
   Content-Security-Policy: suborigin <name>;
Suborigins are a separate field from traditional origins that must be checked whenever the SOP is enforced. Thus, the above suborigin will result in the following SOP origin for its frame:
   origin:    <protocol>://<host>:<port>
   suborigin: <name>
where <protocol>, <host>, and <port> remain as they always have, and <name> is a name defined by the server in the HTTP header.

One of the key properties here is that there is no way to "surrender" or "escape" a suborigin. That is, once an execution context is created, its suborigin cannot be modified (other than destroyed when the execution context ends). Similarly, there is no way to enter a suborigin context other than by a directive from a server in an HTTP response. So, there's no way to point a resource identifier towards a suborigin, for example, because we require that the server be the authoritative source in creating suborigins.

We've considered alternate ways of encoding suborigins, but rejected them for a number of reasons. The design document outlines a bunch of them, so I'll leave that for you to check out on your own time.

There are a bunch of restrictions on suborigins as well that give them interesting properties. The first thing of note is that permissions that are normally associated with origins should not be applied to suborigins. Geolocation and fullscreen permissions, for example, should be segregated from the main origin and a suborigin. Moreover, there should be no way for suborigins to obtain such permissions. This is because we want to avoid, at all costs, users having to be aware of or interact with suborigins in any way. All I can see coming from that is confusion1.

On top of this, we require that suborigins do not have access to document.cookie. This prevents information leaks and limits the damage that compromised frames can do.

The key to Suborigns, however, is that they can use postMessage and listen to message events, allowing them to communicate freely with the main origin. This allows the main origin to act as a type of central manager, doling out privileged information to suborigins at need. Thus, if a suborigin needs to restrict a portion of document.cookie, for example, it can send a message to the main origin which can choose to give (or not to give) that information to the suborigin as needed. This has the potential to help implement a notion of least privilege to help mitigate attacks on a suborigin. See this paper and it's associated Github project (also referenced at the beginning of this post) on design ideas for leveraging least privilege in web applications.

Conclusion

I've left out a bunch of other concerns and design points in this post, but the summary is pretty straightforward. Suborigins look an awfully lot like a named Sandboxed Frame. They would provide a mechanism for creating privilege separation within a web application and between web applications that share an origin, while still allowing them to communicate.

Let us know what you think! Leave comments here, or on the design doc wiki page, or even feel free to shoot me an email (my Chromium account is probably the best place to reach me: jww@chromium.org). This is all in the early stages, and while I'm starting work on an implementation, by no means is anything set in stone.

Finally, a special shout out goes to Devdatta Akhawe who has been extremely helpful in these early designs.


1 I can see a future in which we might allow some less security sensitive permissions to be obtained by suborigins, but it's always easier to grant those at a later time than to take them away.

4 comments:

  1. This is a very interesting proposal. I am curious about this statement:

    > despite being in the same real origin, code from different suborigins would not be able to access the DOM of a different suborigin

    Suppose I have a webpage served without a suborigin that includes a script served with the suborigin 'foo'. Will the script have access to the DOM of the page?

    ReplyDelete
    Replies
    1. Interesting question, but while not clear from my proposal here, I certainly imagine that scripts will execute in the suborigin of the containing page. Thus, it doesn't make sense to talk about a, "script served with the suborigin 'foo'" when the enclosing page doesn't have a suborigin. This is sort of automatically taken care of by implementing suborigins as a CSP directive; CSPs apply to entire pages, and there's no way to apply them to a script independent of the page.

      Another way to think of suborigins is per-frame. Thus, if something (in this case a script) isn't in its own frame, then it won't have a separate suborigin.

      Delete
  2. > I certainly imagine that scripts will execute in the suborigin of the containing page

    Ah, yes, of course they would - just as they execute with the origin of the containing page today. I wasn't thinking carefully about this. Thanks!

    ReplyDelete
  3. This comment has been removed by a blog administrator.

    ReplyDelete