XPConnect security membranes

This article is in need of a technical review.

The XPConnect security membranes are the border lines that separate various regions of authority within the graph of all JavaScript objects in Gecko.

This article is a technical description of the membranes. For an overview, see XPConnect wrappers. For practical advice on dealing with wrappers, see Safely accessing content DOM from chrome.

Introduction

Gecko contains a lot of JavaScript code. In addition, a Gecko application can run JavaScript from the Web. The code in Gecko needs access to resources (browser UI elements, user history and bookmarks, the disk) which must not be exposed to Web scripts. In addition, Web scripts from different domains must be protected from one another (see Same origin policy).

Problems of access control lists. In a typical operating system, each process runs as a particular user. Each time the process attempts to access a system resource, such as a file, the system performs a security check. It compares the process's user id against an access control list to see if access should be permitted. Using this approach in the browser presents several problems:

  • The desired security policy is rather nuanced and fine-grained. An access control list system to implement it would have to be able to enforce separate policies on individual properties of a single JavaScript object.
  • Access control by itself does not address certain problems that arise due to the dynamic nature of JavaScript. Suppose browser code has access to a window object and it wants to look at a DOM node in a Web page. It executes window.document.getElementById("header"). This being JavaScript, the Web page in question may have replaced its document.getElementById with another function entirely. It might return a different DOM node or a JavaScript object that merely behaves like a DOM node. The danger here is indirect: a system based on access control lists could enforce its policy correctly, but the browser code still can't trust the answers it gets back from DOM calls. The Web page can mislead the browser code.
  • It would require a security check every time a script attempted to access a property of a DOM node. The number of security checks would be so great as to slow down the browser.

Overview of object capabilities. Gecko uses an object-capability security model instead. By default, when code accesses objects, there are no security checks. A reference to an object is essentially permission to access all properties and methods of that object.

So the graph of all JavaScript objects is divided into compartments, regions in which all objects should be fully accessible to a certain user. For example, all the objects in Web pages for a given domain, including DOM objects, reside in the same compartment. Objects for other domains live in separate compartments, and objects representing things like the user history and filesystem access reside in yet another compartment. Code that belongs in a given compartment always runs within that compartment and operates exclusively on the objects in that compartment.

object-graphs.png

What would happen if compartmentalization were to break down? Suppose domain 2 obtained a reference to object in the chrome compartment. Then code in domain 2 could access the properties and methods of that object without security checks. Furthermore, domain 2 could use those properties to obtain references to other chrome objects. So domain 2 would have unconstrained access to every chrome object reachable from the first one. (Note that though it is not drawn this way, almost every object in a graph holds a strong reference to the corresponding global object through the object's scope chain.)

Conversely, suppose chrome obtained a reference to an object in domain 2. This could be harmless as long as chrome treated the object with great care. But there are several major dangers. First, any time the chrome object touches the object from domain 2, a getter or setter might be called. Second, as discussed above, chrome cannot trust any information it gets from the object, since methods and properties with standard behavior may have been overwritten. Third, the leak naturally widens: any objects returned by the leaked object's methods or getters will also likely be from domain 2; and any objects assigned to properties of the leaked object, or arguments chrome passes to its methods, cause domain 2 to have references back to chrome objects.

Therefore, when an object must be exposed to code running in a different compartment, it is exposed only by proxy. A wrapper object is introduced into the compartment. The wrapper object holds a strong reference (in the garbage collection sense) across the compartment boundary, but it also enforces the appropriate security policy, so code in that compartment does not necessarily have unrestricted access to the underlying object. In the illustration below, a chrome object has been exposed to code in domain 1.

proxy.png

Membranes. Suppose a privileged compartment calls a method on an object in another compartment. Consider how this method call must proceed, in order to keep the compartments isolated.

The underlying method will of course run in the underlying object's compartment. So any objects that are passed to the method as arguments must not be passed directly. That would violate compartment isolation. Instead they must be proxied for that compartment. It is likely that one or more arguments were already wrappers of objects from that compartment. This is at least true for the this argument, ordinarily. Those objects should be unwrapped, since they are being passed back into their compartment of origin.

When the method returns, if the return value is an object, it must be wrapped or unwrapped for the caller's compartment. The same goes for any exception thrown.

So as methods are called back and forth, the wrappers spread out as needed along the boundary to keep the compartments separate. This slim, porous, but non-pierceable layer of wrapper objects is what we mean by a membrane. The membrane defines and implements the boundary between compartments.

Wrapper types

All wrappers have a lot of behavior in common. Every wrapper wraps a single object, for example, and the wrapper never changes to wrap something else. When calling a getter, setter, or method of a wrapper, the wrapper always takes certain steps to keep the membrane whole (that is, to avoid leaking objects across the boundary). (But see bug 565730 and bug 565735.)

Wrapper types differ in three ways.

  • Access policy - Some wrappers check, on each property access, whether the compartment where the wrapper lives is permitted to access that property of the wrappee. Some don't. Those that do enforce a variety of policies.
  • Delegation - When you get or set a property of a wrapper, sometimes that operation is transparently delegated to the wrappee. In other cases, the operation is actually delegated to an underlying XPCOM object instead. For details on this, see the section on XPCNativeWrapper below.
  • Reverse wrapper - Every membrane has two sides. As described above, values passed to and from a wrapper's methods, getters, and setters must be rewrapped for the destination compartment. Different wrappers rewrap objects passed as arguments in different ways.

(Warning: This is too optimistic. XPCNativeWrapper differs in an additional way, in that xpcnw.foo = 1 creates a new own property of xpcnw itself. The others never really have own properties. And SystemOnlyWrappers are rather different from the other four types too.)

The table below explains (most of) the differences among the security wrapper types.

 Type Wraps... For... Access policy Delegation Reverse
XPCNativeWrapper (XPCNW) content chrome, usually checks through security manager X-ray COW
SafeJSObjectWrapper (SJOW) content chrome, usually checks through security manager transparent COW
ChromeObjectWrapper (COW) chrome content __exposedProps__ transparent XPCNW
CrossOriginWrapper (XOW) content content same origin policy plus whitelist

X-ray, usually; but transparent if same-origin

XOW
SystemOnlyWrapper (SOW) native anonymous content everyone by source URI transparent none

XPCNativeWrapper

XPCNativeWrapper will only wrap XPCWrappedNatives, and will throw an exception if it is asked to wrap any other type of JavaScript object (see the section about double wrapping for more information about this statement).

XPCNativeWrappers are designed to protect chrome from the dynamicism that JavaScript provides to content. For example, consider the chrome code someAnchor.href = "http://www.mozilla.com" operating on a content element. The content code may have defined an evil setter on someAnchor.href to prevent this from working correctly. If someAnchor is wrapped by an XPCNativeWrapper, this will not happen. The wrapper will always perform the original action, calling the SetHref method of the underlying XPCOM object.

XPCNativeWrappers are implicitly created by XPConnect for chrome JavaScript or JavaScript components, whenever they access content (less privileged) objects. "Expando" properties, not defined in IDL, but created by the content on the object, are not visible through this wrapper.

See XPCNativeWrapper and Safely accessing content DOM from chrome for more information on this topic.

membranes-2.png

Intra-compartment XPCNW. XPCNativeWrapper objects can also be created explicitly by using new XPCNativeWrapper(wrappedNative). In this case, the XPCNW does not act as a security wrapper. Both the wrapper and the wrappee are in the same compartment, so the security aspect of the XPCNW is moot. This is useful only to get the x-ray behavior.

XPCSafeJSObjectWrapper

This wrapper was created to address some problems with XPCNativeWrapper. In particular, some extensions want to be able to safely access non-natively-implemented content defined objects (and to access the underlying JavaScript object under an XPCNativeWrapper without its strong behavior guarantees). SJOW act as a buffer between the chrome code.

Note: Because any type of object can be wrapped here, there is no predefined behavior. The only guarantee here is that untrusted code will run in an untrusted context. In particular, the underlying object might act entirely differently from what is expected.

Safe JSObject wrappers are implicitly created in some circumstances. All properties are visible through this wrapper.

XPCCrossOriginWrapper

The cross-origin wrapper, or XOW, brokers access between web sites. This wrapper is implicitly created by XPConnect.

If a XOW wrapper and the object it wraps happen to be from the same origin, then the XOW acts like a SafeJSObjectWrapper. It becomes as transparent as possible.

If the underlying object is not from the same origin, then the XOW checks every property access against a short whitelist of properties and methods that are exposed across domains, as special exceptions to the Same Origin Policy. When access to a property or method is granted, the XOW acts like an XPCNativeWrapper: it calls the method or accessor of the underlying XPCOM object, as defined by IDL, seeing through whatever the other site may have done to the JS object.

membranes-1.png

Attachments

File Size Date Attached by
membranes-1.png
28231 bytes 2010-03-17 16:05:16 Jorend
membranes-2.png
38024 bytes 2010-03-17 16:04:51 Jorend
object-graphs.png
41634 bytes 2010-03-17 18:04:43 Jorend
proxy.png
19808 bytes 2010-03-18 11:03:36 Jorend
Size harmonization on Chrome
This is a screenshot of the main form widgets on Chrome on Windows 7, with and without the use of box-sizing.
3452 bytes 2012-11-05 03:19:51 Jeremie
Check box on Chrome
Rendering of a sized check box on Chrome
421 bytes 2012-11-05 10:00:20 Jeremie
Select on Firefox (7ON)
Select open on Firefox on Windows 7 (No tweak)
1301 bytes 2012-11-06 02:15:16 Jeremie

Document Tags and Contributors

Contributors to this page: Sheppy, fscholz, Waldo, Jorend, kscarfone
Last updated by: Sheppy,