Wrappers are not a new concept in Firefox, or browsers in general. In the past we have already used them to regulate how windows (or tabs) pass objects to each other. In the past, when another window (or tab or iframe) tried to touch an object that belongs to a different window, we handed it a wrapper object instead. That wrapper object dynamically checks at access time whether the accessor window (also called the subject) is permitted to access the target object. If one Google Mail window is trying to access another Google Mail window, the access is permitted, because these two windows (or tabs or iframes) are same origin and hence its safe to permit this access. If an untrusted website obtains a reference to a Google Mail DOM element, we hand it the same wrapper, and if it ever tries to access the Google Mail DOM Element the wrapper will at access time deny the property access because the untrusted website “evil.com” is cross origin with “google.com”.
A disadvantage of the Firefox 3.6 wrapper approach (which is similar to the way other browsers utilize wrappers) was the fact that these wrappers had to be injected manually at the right places in the C++ browser implementation, and each wrapper had to do a dynamic security check at access time. With compartments we can do a lot better:
- Since all objects belonging to the same origin are within the same compartment, and no object from a different origin is in that compartment, we can let all objects within a compartment touch other objects in the same compartment without a wrapper in between. Keep in mind that this doesn’t just apply to windows but also to iframes. A single Google Mail session often uses dozens of iframes that all heavily exchange objects with each other. In the past we had to inject wrappers in between that kept performing dynamic security checks. This is no longer necessary, and there is an observable speedup when using iframe heavy web applications such as Google Mail.
- Since all cross origin objects are in a different compartment, any cross origin access that needs to perform a security check can only happen through a cross compartment wrapper. Such a cross compartment wrapper always lives in a source compartment, and accesses a single destination object. When we create a cross compartment wrapper, we consult with the wrapper factory to see what kind of security policy should be applied. When “evil.com” obtains a reference to a “google.com” object, for example, we have to create a wrapper to that object in the “evil.com” compartment. When that wrapper is created the wrapper factory will tell us to apply a stringent cross origin security policy, which makes it impossible for “evil.com” to glean information from the “google.com” window. In contrast to our old wrappers, this security policy is static. Since only “evil.com” objects ever see this wrapper, and it only points to one single DOM element in the destination compartment, the policy doesn’t have to be re-checked at access time. Instead, every time “evil.com” attempts to read information from the DOM element, the access is denied without even comparing the two origins.
We solve this problem through brain transplants. Whenever an outer window navigates, we copy it into the new destination compartment. The object in the old compartment is transformed into a cross compartment wrapper that points to the newly created object in the destination compartment. So the term brain transplants is very appropriate here. We are essentially transplanting the guts of the outer window object into a new object hull in the same compartment we allocated the inner object in.