COWL: A Confinement System for the Web

About

Modern web applications are conglomerations of JavaScript written by multiple authors: application developers routinely incorporate code from third-party libraries, and mashup applications synthesize data and code hosted at different sites. In current browsers, a web application's developer and user must trust third-party code in libraries not to leak the user's sensitive information from within applications. Even worse, in the status quo, the only way to implement some mashups is for the user to give her login credentials for one site to the operator of another site. Fundamentally, today's browser security model trades privacy for flexibility because it lacks a sufficient mechanism for confining untrusted code once it reads sensitive data.

COWL (Confinement with Origin Web Labels) is a robust JavaScript confinement system for modern web browsers. COWL introduces label-based mandatory access control to browsing contexts (pages, iframes, etc.) in a way that is fully backward-compatible with legacy web content. With COWL, developers not only can restrict with whom they share data, but also can impose restrictions on how their data is disseminated once it is shared. COWL achieves both flexibility for developers and privacy for users: it allows code to fetch and share data as necessary, but once code has read sensitive data, COWL confines the code by revoking its right to communicate with unauthorized parties.

Learn more

W3C specification

Confinement with Origin Web Labels. Stefan, D. [FPWD | source]

Academic paper

Protecting Users by Confining JavaScript with COWL. Stefan, D., Yang, E., Marchenko, P., Russo, A., Herman, D., Karp, B., and Mazières, D. In the Proceedings of the 11th USENIX Symposium on Operating Systems Design and Implementation (OSDI 2014) , Broomfield, CO, October, 2014. [slides | bibTex]

Closely-related work

The Most Dangerous Code in the Browser. Heule, S., Rifkin, D., Stefan, D., and Russo, A. In the Proceedings of the Workshop on Hot Topics in Operating Systems (HotOS), Kartause, Ittingen, Switzerland, May, 2015. [ bibTex | slides ]

IFC Inside: Retrofitting Languages with Dynamic Information Flow Control. Heule, S., Stefan, D., Yang, E., Mitchell, J., and Russo, A. In the Proceedings of the Conference on Principles of Security and Trust (POST), London, UK, April, 2015. [ bibTex | extended version ]

Toward Principled Browser Security Yang, E., Stefan, D., Mitchell, J., Mazières, D., Marchenko, P., and Karp, B. In the Proceedings of the 14th USENIX Workshop on Hot Topics in Operating Systems (HotOS XIV), Santa Ana, NM, May, 2013. [slides | bibTex]

Software

We have implemented COWL as a new DOM-level API for the Firefox and Chromium browsers. The preliminary version of our Firefox COWL implementation is available for download. Note: the implementation is as described in the paper, not the spec.

Binaries

Source

You can access the source on GitHub, or by simply running:

$  git clone https://github.com/scslab/cowl.git

Case-studies

We have implemented several case-studies. We will upload them to this page, incrementally, as tutorial-like pages. For now, checkout:

About us

COWL is a collaboration among several researchers from several organizations. We are:

Design

The COWL design naturally extends the existing web security model with three core primitives on which developers can build privacy-preserving web services. These primitives are:

Labeled contexts

Today, developers use contexts (e.g., tabs, pages, and iframes) to isolate content from different origins (web sites). COWL extends contexts with labels, which encode the origins from which a context has read information. COWL then uses these labels to restrict how code within a context communicates: it confines the code to disallow any communication that would result in leaking an origin's sensitive data. COWL fits well with existing legacy web sites because it imposes these restrictions within the framework of existing discretionary access control (DAC) policies, such as the same-origin policy and CSP.

Labeled communication

To impose restrictions on how third-party code can use sensitive data, developers can label data before handing it off to a third-party context (e.g., using postMessage). When the third-party code later decides to use the data, COWL "taints" its context's label to confine the executing code. This approach differs from the status quo, in which the developer must effectively choose between benefiting from the functionality of the third-party code or risking the leaking of the sensitive data. With COWL, a developer can safely use third-party libraries by ensuring they are confined once granted access to sensitive information.

Privileges

In today's model, when the browser retrieves a page from https://example.com, any script within the context for the page is trusted not to violate the security concerns of https://example.com. This is natural: https://example.com should be allowed to dictate how its data is disseminated! In COWL, this notion of trust is made explicit with privileges. Privileges enable code to act on behalf of a page's origin and thus avoid becoming confined when reading data sensitive to that origin. Equally importantly, however, COWL allows code to "drop" its privileges or delegate them to other trustworthy code. Developers can use this primitive to build applications that adhere to the principle of least privilege.

These simple three primitives are amenable to a fast browser layout engine-level implementation, without any modifications to the underlying JavaScript engine. Moreover, because they extend already familiar concepts (e.g., contexts, postMessage, and XMLHttpRequest), we believe developers will find them a natural means for exerting additional control over the privacy of the data they curate. An example (see link above) illustrates how the COWL API can be used to confine a third-party password strength-checker library; our paper details three other common application design patterns.

Example: Confining a password strength-checker with COWL

COWL can be used to build several common types of application securely. Our paper describes several examples, including an encrypted document editor, an app that relies on jQuery, a third-party mashup, and a password strength checker. We will release the code for all of these soon, but for now let's see how we can confine a third-party library such as a password strength-checker.

Suppose the developer of the page https://example.com wants to use the password strength checker from http://sketchy.ru/checker.js. Maybe the developers at sketchy.ru are not malicious, but the developer doesn't trust them to write bug-free code. Note that the checker may need to communicate with sketchy.ru to fetch data that it needs to do its job. (For a more realistic library, e.g., the syntax highlighter running in this page, this is desirable functionality.) How does the developer use COWL to ensure that the checker won't leak a user's password to sketchy.ru or any other site?

Step 1: load the checker in a new labeled context

   // In example.com page, create new context:
   var checker = new LWorker("http://sketchy.ru/checker.js");
An LWorker is a lightweight labeled worker, COWL's approach to creating cheap labeled contexts. (These are similar to Web Workers, but labeled and run in the same thread as the parent.) This code simply creates a new context and starts running the untrusted checker code in this context. (Note: If you are using the preliminary release of COWL, to run this code you will need to include cowl.js to use the LWorker API.)

Step 2: register handler to get result

   // In example.com page, register message handler waiting for result
   checker.onmessage = function(result) {
     console.log('Password is: ' + result.toString());
   };
Since all communication between labeled contexts is done by message passing, we register a handler to be invoked once the checker sends us the result.

Step 3: send password

   // Is the password sensitive? Yes, label it with example.com!
   var labeledPassword = new LabeledBlob(password, "http://example.com");
   // Send the checker the labeled password:
   checker.postMessage(labeledPassword);
Finally, let's send the checker the password so it can do its job. Since the password is sensitive, though, we label it with the origin it's sensitive to: example.com. Now when when the checker reads the password, it will be confined!

The checker code

Our code expects the checker to use message passing. What might this code look like? Here is one possibility:

  // In checker.js, register handler waiting for request from parent:
  onmessage = function(labeledPass) {
    if (doneLoading) {
      // Taint contex to preserve privacy or password
      COWL.privacyLabel = COWL.privacyLabel.and(labeledPass.privacy);
      // Now we can read the password
      var password = labeledPass.blob;
      // Cannot communicate with sketchy.ru anymore, but can reply to parent:
      postMessage(checkStrength(password));
    }
  };
  // .. use XHLHttpRequest to communicate with arbitrary sites
This checker code starts out unconfined: it can communicate with arbitrary web sites to fetch any data it needs. However, once it has finished doing so (indicated by doneLoading) and the parent has supplied it the labeled password, it can proceed to check the strength of the password. Specifically, in the onmessage handler, the code extracts the password from the labeled blob--at which point COWL taints the context--and computes the strength with the checkStrength function. Once the code has inspected the password COWL confines it, preventing it from communicating arbitrarily. The code can, of course, send the containing example.com page the strength of the password.

Take-away: privacy AND functionality

Even this simple example illustrates how COWL achieves two properties that are today generally mutally exclusive: the password strength checker has the flexibility to communicate as necessary, but once it actually has inspected the password, it is thereafter prevented from communicating arbitrarily so as to ensure privacy. Today developers can either opt for functionality by allowing the checker to communicate arbitrarily (even after reading sensitive data) or privacy by disallowing it from communicating at all (or not using the library in the first place).

What's next?

Checkout the case study that goes into this password strength-checker example in more detail, with actual running code.