July 29, 2009

Flexible Membranes and Catch-alls in JavaScript

One of the recurring issues that the Mozilla platform team has to contend with is the issue of how to allow trusted, privileged JavaScript code to interact with untrusted JavaScript code. Google’s Caja team actually has to deal with a very similar problem, albeit at a different layer in the technology stack.

This issue is quite subtle, and fully explaining it is beyond the scope of this blog post. If you know JavaScript, I recommend checking out the Caja Specification, which nicely lays out the problems inherent in running code with different trust levels in the same environment.

Firefox has to deal with this issue because much of it is actually written in JavaScript. Developers call the JS that powers Firefox chrome JavaScript: it has the ability to write to the filesystem, launch other programs on your computer, and pretty much anything that Firefox itself can do. The code that runs in web pages, on the other hand, is called content JavaScript. Chrome and content JS can interact with each other securely thanks to XPConnect wrappers: little layers of code that “wrap” objects and mediate access between them and the outside world. The self-proclaimed WrapMaster and implementer of most of these wrappers is Blake Kaplan, known in some circles as “Mr. B-Kap” (mrbkap).

Google Caja’s team also has a need for the same kind of functionality, but at a different level: they need to make it possible for web pages themselves able to run code that they don’t trust, which is useful when creating plug-in frameworks for web applications. The Caja team calls wrappers membranes—a word which I find more intuitive than “wrappers” because it’s not an overloaded term in computer science and because its biological definition closely matches that of its CS counterpart.

As I wrote in Jetpack: Summer 2009 State of Security, Part 1, the boundary between trusted and untrusted code has been of some concern to the Jetpack project. Unfortunately, all the XPConnect wrappers currently in Firefox have very specific purposes: for instance, most of them are made expressly to prevent omnipotent chrome code from being exploited by impotent content code. Jetpack’s needs are unique in that a Jetpack feature should be neither as omnipotent as Firefox, nor as impotent as a web page: ideally, we should follow the principle of least privilege and give it the minimum set of capabilities it needs to do its task, and no more.

After talking with the Firefox JS and Google Caja teams, we decided that wrappers were the right kind of solution to Jetpack’s security challenges. The problem was, though, that all of Firefox’s wrappers are in C++, which made them hard to experiment with. Jetpack is, after all, a Labs project, and as such, we needed a sort of “flexible membrane” whose security characteristics we could easily change as the platform evolved. So we decided to expose some functionality to chrome JavaScript that’s traditionally only available to C/C++ code.

One nice aspect of the flexible membranes we’ve created is that they’re useful for more than just prototyping membranes: they effectively allow chrome JS to create objects with characteristics that the JavaScript language doesn’t traditionally make room for, like catch-alls for object properties. Python programmers know of these by names like getattr and setattr, and many other dynamic languages have them, but JavaScript doesn’t—yet something like them is needed to implement basic Web APIs like HTML5 localStorage. In other words, these flexible membranes should make it easy for us to develop nicer APIs for Jetpack.

If you’re interested in digging into these flexible membranes, check out our Binary Components documentation on the wiki. And feel free to take the pre-compiled component from our HG repository and use it in your own Firefox extensions.

© Atul Varma 2021