XPConnect security membranes

  • Revision slug: XPConnect_security_membranes
  • Revision title: XPConnect security membranes
  • Revision id: 114759
  • Created:
  • Creator: Jorend
  • Is current revision? No
  • Comment 44 words added, 13 words removed

Revision Content

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

{{ Fx_minversion_inline("3") }} 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

{{ Fx_minversion_inline("3") }} 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

Revision Source

<p>The <strong>XPConnect security membranes</strong> are the border lines that separate various regions of authority within the graph of all JavaScript objects in Gecko.</p>
<p>This article is a technical description of the membranes. For an overview, see <a href="/en/XPConnect_wrappers" title="en/XPConnect wrappers">XPConnect wrappers</a>. For practical advice on dealing with wrappers, see <a class="internal" href="/en/Safely_accessing_content_DOM_from_chrome" title="Ja/Safely accessing content DOM from chrome">Safely accessing content DOM from chrome</a>.</p>
<h2 id="Introduction">Introduction</h2>
<p>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 <a href="/en/Same_origin_policy_for_JavaScript" title="en/Same origin policy for JavaScript">Same origin policy</a>).</p>
<p><strong>Problems of access control lists.</strong> 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:</p>
<ul> <li>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.</li> <li>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 <code>window.document.getElementById("header")</code>. This being JavaScript, the Web page in question may have replaced its <code>document.getElementById</code> 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 <em>can't trust</em> the answers it gets back from DOM calls. The Web page can <em>mislead</em> the browser code.</li> <li>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.</li>
</ul>
<p><strong>Overview of object capabilities.</strong> Gecko uses an <a class=" external" href="http://en.wikipedia.org/wiki/Object-capability_model" title="http://en.wikipedia.org/wiki/Object-capability_model">object-capability security model</a> 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.</p>
<p>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 <em>always</em> runs within that compartment and operates exclusively on the objects in that compartment.</p>
<div style="margin: 1em 2em"><img alt="object-graphs.png" class="internal default" src="/@api/deki/files/4196/=object-graphs.png"></div>
<p>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 <em>without security checks</em>. 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.)</p>
<p>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.</p>
<p>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.</p>
<div style="margin: 1em 2em"><img alt="proxy.png" class="internal default" src="/@api/deki/files/4207/=proxy.png"></div>
<p><strong>Membranes.</strong> 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.</p>
<p>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 <em>were already wrappers</em> of objects from that compartment. This is at least true for the <code>this</code> argument, ordinarily. Those objects should be <em>unwrapped</em>, since they are being passed back into their compartment of origin.</p>
<p>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.</p>
<p>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.</p>
<h2 id="Wrapper_types">Wrapper types</h2>
<p>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") }}.)</p>
<p>Wrapper types differ in three ways.</p>
<ul> <li><strong>Access policy</strong> - 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.</li> <li><strong>Delegation</strong> - 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.</li> <li><strong>Reverse wrapper</strong> - 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.</li>
</ul>
<p>(Warning: This is too optimistic. <code>XPCNativeWrapper</code> differs in an additional way, in that <code>xpcnw.foo = 1</code> creates a new own property of <code>xpcnw</code> itself. The others never really have own properties. And <code>SystemOnlyWrappers</code> are rather different from the other four types too.)</p>
<p>The table below explains (most of) the differences among the security wrapper types.</p>
<table class="fullwidth-table"> <tbody> <tr> <th> Type</th> <th>Wraps...</th> <th>For...</th> <th>Access policy</th> <th>Delegation</th> <th>Reverse</th> </tr> <tr> <td><code>XPCNativeWrapper</code> (XPCNW)</td> <td>content</td> <td>chrome, usually</td> <td>checks through security manager</td> <td>X-ray</td> <td>COW</td> </tr> <tr> <td><code>SafeJSObjectWrapper</code> (SJOW)</td> <td>content</td> <td>chrome, usually</td> <td>checks through security manager</td> <td>transparent</td> <td>COW</td> </tr> <tr> <td><code>ChromeObjectWrapper</code> (COW)</td> <td>chrome</td> <td>content</td> <td><code>__exposedProps__</code></td> <td>transparent</td> <td>XPCNW</td> </tr> <tr> <td><code>CrossOriginWrapper</code> (XOW)</td> <td>content</td> <td>content</td> <td>same origin policy plus whitelist</td> <td> <p>X-ray, usually; but transparent if same-origin</p> </td> <td>XOW</td> </tr> <tr> <td><code>SystemOnlyWrapper</code> (SOW)</td> <td>native anonymous content</td> <td>everyone</td> <td>by source URI</td> <td>transparent</td> <td>none</td> </tr> </tbody>
</table>
<h3 id="XPCNativeWrapper">XPCNativeWrapper</h3>
<p><a href="/en/XPCNativeWrapper" title="en/XPCNativeWrapper">XPCNativeWrapper</a> 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).</p>
<p>XPCNativeWrappers are designed to protect chrome from the dynamicism that JavaScript provides to content. For example, consider the chrome code <code><span class="nowiki">someAnchor.href = "http://www.mozilla.com"</span></code> 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 <code>SetHref</code> method of the underlying XPCOM object.</p>
<p>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.</p>
<p>See <a href="/en/XPCNativeWrapper" title="en/XPCNativeWrapper">XPCNativeWrapper</a> and <a href="/en/Safely_accessing_content_DOM_from_chrome" title="en/Safely_accessing_content_DOM_from_chrome">Safely accessing content DOM from chrome</a> for more information on this topic.</p>
<p><img alt="membranes-2.png" class="internal default" src="/@api/deki/files/4163/=membranes-2.png"></p>
<p><strong>Intra-compartment XPCNW.</strong> XPCNativeWrapper objects can also be created explicitly by using <code>new XPCNativeWrapper(wrappedNative)</code>. 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.</p>
<h3 id="XPCSafeJSObjectWrapper">XPCSafeJSObjectWrapper</h3>
<p>{{ Fx_minversion_inline("3") }} 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.</p>
<p>{{ 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.") }}</p>
<p>Safe JSObject wrappers are implicitly created in some circumstances. All properties are visible through this wrapper.</p>
<h3 id="XPCCrossOriginWrapper">XPCCrossOriginWrapper</h3>
<p>{{ Fx_minversion_inline("3") }} The cross-origin wrapper, or XOW, brokers access between web sites. This wrapper is implicitly created by XPConnect.</p>
<p>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.</p>
<p>If the underlying object is <em>not</em> 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.</p>
<div style="margin: 1em 2em"><img alt="membranes-1.png" class="internal default" src="/@api/deki/files/4161/=membranes-1.png"></div>
Revert to this revision