Security wrappers, part 1: Basics

0

When I joined the Jetpack team half a year ago, the first bugs I started to work on were related to security wrappers and reflectors. I have to admit that I have never ever in my life had a more challenging start at a new company than this. Soon I discovered that xpconnect is about as popular a part of the Mozilla platform as IE6 in the web developer community. Yes it is messy. And yes it is complex. But let’s face it, it is one of the most important core parts of the platform.

I strongly believe that many new platform hackers will find this article useful. But not just c++ hackers should be interested in this topic. Security comes with restrictions. And many folks out there are confused when they bump into an unexpected behavior in javascript that is caused by some mysterious wrappers.  In the meanwhile I know from experience how difficult is to get any information about them, so Eddy Bruel and I visited Blake Kaplan in Paris to learn more about wrappers to spread the word :)

This is going to be a long journey. Instead of writing a gigantic article that covers _everything_, I’m going to write shorter blogs about different topics around xpconnect. So let’s start with the basics. There are 2 articles I know of on wrappers and I think they are a good start: https://developer.mozilla.org/en/XPConnect_wrappers and https://developer.mozilla.org/en/XPConnect_security_membranes. There is only one problem with them: they are a bit outdated. Ok there is something else too… just by reading these articles don’t even dream about finding it easy to find your way in the code, or to debug it… But I’ll get back to that on another post. I hope someone will find my additions useful.

Let’s start with the distinction of wrappers and reflectors. The term reflector is not really documented yet anywhere. But since the term wrapper was used for two completely different concepts, to avoid confusion this new term was invented and used ‘in house’. So reflectors are responsible for the transition between javascript and c++. So XPCWrappedNative is wrapping a native object and expose it to javascript, and XPCWrappedJS is responsible for the other direction, wrapping a javascript object and let native C++ code operate on it.

Before we talk about security wrappers, let’s talk about principals for a second. Principals are our security tokens, determining what a script can or cannot do. A typical principal is comprised of an URL scheme, host, and port. So if an object wants to do some operation on another object from a different script, the principals of the two scripts will be compared to allow or deny the operation. If the script of our site wants to have access to some object of an iframe, we check if the principal of the site and iframe are same origin, and if so, we can apply the same origin policy, otherwise we have to be much more restrictive.

There are two special principals as well. The system principal that has same origin rights to any script with any other principal, and even can access system level API as well. Scripts with system principal are referred to as chrome code, scripts with regular principal are content code. So chrome code is usually code, written by mozillians, or some addon developers, operating often at browser level (UI for example), or even system level (managing threads, monitoring memory usage, etc). The other one is the nullprincipal, that has minimal privileges, so it never has same origin policy, not even with other scripts that have nullprincipal as well.

Security wrappers (from here on I will refer to them simply as wrappers) are our shield against all the “evil” sites out there, limiting what we can do with the wrapped objects. This first article will be about the security wrappers. So referring back to the previous example, a security wrapper will contain the logic (code), that actually check the two principals against each other and throw an error if the operation should be denied.

The idea of security wrappers in general is that we don’t want objects have direct, uncontrolled access cross origin, and even less between chrome and content scripts.

Let’s speak a few word about how is this all implemented. So objects from the same origin live in their own separated chunk of memory, called the compartment. Each of these compartments have their own principal (and own global object as well). So in general we don’t let an object have direct access to another object from a different compartment. Instead, when an object is exposed to another script, we create a proxy object in the compartment that belongs to that other script.

A proxy is an object that delegates the operations to the object in the original compartment it was created for. A security wrapper is simply a filtering proxy, with some policy. The policy tells in a certain case whether the operation should be allowed or denied. Before delegating the requested operation the proxy first checks if according to its policy it should allow it or if not it throws an exception or fails silently.

For example: my script has a reference to the document element of an iframe, and I want to get its children. What will happen under the hood is that in my script’s compartment there will be a filtering proxy object created, and when I access a property on it to fetch the children, assuming its policy allows it, the proxy will delegate the call to the underlying object in the other compartment.

Wrappers between chrome and content

ChromeObjectWrapper (COW), XrayWrapper (XPCNativeWrapper) and XPCSafeJSObjectWrapper (SJOW)

If we would let any site access some system level API only chrome code should have access to, this would be a major security problem. Even if some unsafe code is able change something on the base classes of a system compartment (String, Object, etc.) there could also be dangerous side effects. So when some object has to be exported from chrome to web content the security wrapper we create is called the ChromeObjectWrapper, which is the filtering wrapper in the content compartment.

The other side of the story is when our chrome code is accessing objects from some content code, typically DOM elements and other WrappedNatives. In this case we expect certain behavior from the object, but javascript can be sometimes too flexible. If untrusted sites were capable of overwriting methods on DOM elements, it could trick us to call their possible evil code from chrome context. On the other side users of course want to be able to overwrite properties and methods on the DOM elements.

This is where XrayWrapper enters the picture. XrayWrapper detects the native object on the content side, ignores every javascript based modification from content side on it (overwritten properties, getters, etc.), and calls right the native implementation of the method. So it comes handy that those DOM elements are implemented in C++, and when it is exposed to javascript, it is wrapped in a JSObject, and all the modifications and extensions happen on this JSObject, so we can just simply ignore those. Even in cases where, on content side, the getter of the window.location is overwritten, when chrome code tries to access the property via an XrayWrapper it will use the original native implementation of the location getter. So here the “policy” of the security wrapper is quite special, and not really principal driven. In fact, XrayWrapper is a very special security wrapper, even its implementation is separated from the rest of the security wrappers.

Note: XrayWrapper used to be called XPCNativeWrapper, and in some places of the code (constructor) and in most of the docs they are still called like that. This is confusing and it makes it even worse that WrappedNative is a completely different thing with very similar name, but hey, I warned you upfront that it gets a bit messy sometimes…

Some more magic: there is a case when XrayWrappers are too restrictive and we want to have access to the underlying object (on our own responsibility) without these limitation,  for example, a debugger wants to access some global variables of the debugged code. Here we can use the so called wrappedJSObject property on the wrapper which will unwrap the underlying object and use a completely transparent wrapper. At this point I hope I don’t have to explain why is this unsafe, but in some cases it can be useful. It basically goes from very secure to “watch your steps” mode, as in: chrome has to be very careful about the assumptions it makes about the functions it calls, since they can now do anything and return any sort of value. So simply calling a function or getting a property will not give chrome privileges away. So it cannot happen that we call a content defined function from chrome (or it can be a getter too) and the function is executed with chrome privileges accidentally. But for example content can navigate the page or do something else it had originally permission to do, when we expect it to do something completely different.

In general you want to avoid using this property, and if you really need this functionality you should use XPCNativeWrapper.unwrap, since it will work even if your object is not an XrayWrapper (I repeat: XPCNativeWrapper == XrayWrapper). Implementation wise, this function wraps the object in it’s own compartment in a so called WaiveXrayWrapperWrapper, which is a fully transparent wrapper, and unlike wrappers so far the proxy and the original object are in the same compartment. And then when an XrayWrapper is created for it in the chrome compartment, this wrapper will be detected and we won’t apply an XrayWrapper, more precisely a transparent wrapper called  SafeJSObjectWrapper (see below) will be applied.

The reason why you should prefer the unwrap function over the wrappedJSObject property is because just to make it more confusing wrappedJSObject might mean something else. It can be just a simple property of course, or, if there is a (COM) component implemented in javascript, then instead of the object itself a wrapper of it will be exposed to javascript that is using the component. This way it will act like any other native implementation of the component (interfaces). So the caller will not have access to anything other then the original implementation of the interfaces. So in some cases we want to access the underlying javascript object directly, and for some reason we use wrappedJSObject property here too. These looks like a confusing naming conflict… and it is. So this second use of wrappedJSObject was first, and unfortunately I don’t quite know the story behind it. But the symmetry behind the two cases is pretty obvious, so it’s not hard to guess why was the same name reused in XPCNativeWrapper. Anyway in practice I think this can be quite confusing if we are not aware of it.

And there is a 3rd wrapper in this category which is a little bit harder to grasp, it’s the SafeJSObjectWrapper (SJOW).  It is for the case where chrome wants to access a non-native object from web content or in some cases where we want to avoid using the restricting XrayWrapper. Now what we want to avoid here is some property getter function being executed in a chrome privileged context accidentally. It’s simply a transparent wrapper and its only purpose is to prevent some content defined function to be executed with chrome privileges, what you should never want to do anyway.

Note: In the code it is called: CrossOriginWrapper, and it is NOT the wrapper that is dealing with the case when content from one origin is exposed to a content from another origin and referred to as XOW  (Cross-OriginWrapper in the docs).

Wrappers between content and content

Cross-OriginWrapper (XOW)

There are cases where content needs to interact with other content, for example when a script interacts with some objects from an iframe. In this case the Cross-OriginWrapper will be applied. We distinguish two cases. The first and simplest case when both scripts have the same origin (the iframe is from the same domain as the original for example). In this case the wrapper is as transparent as possible. In the second case the domains are different, it will be more restrictive. It is basically the combination of an XrayWrapper and a short hardcoded white list of allowed property accesses.

Note: It is important to keep in mind when looking at the actual code, that CrossOriginWrapper class is totally different, and we are talking about  XOW here (which is unfortunately the short term for the same name) and it is a filtering proxy defined in FilteringWrapper.cpp.

SystemOnlyWrapper (SOW)

And the last case is a very special case. When native anonymous content is exposed to JS, it will be wrapped into a SystemOnlyWrapper, which denies every access unless it comes from chrome. This was introduced for XBL so that XBL elements when cloned into a web page can access native anonymous content from JS (which is not even visible normally from regular content).

Now that we have some grasp of each security wrapper’s role let see  what restrictions they come with.

Restrictions per wrapper:

XrayWrapper

From a javascript developer perspective, XrayWrappers are the most tricky ones. In a nutshell: if the underlying object is a native property, it can be called or accessed.  Expando properties are allowed on the wrapper, which means you can set a new property like myXrayWrappedObject.foo = 42; and that  property will be visible from the chrome side, but not from the content side (since it will be set on the wrapper instead of the actual object the wrapper proxy to). This can be a very confusing behavior. Especially that some other properties that might have a native getter, will operate directly on the content… If that is not confusing enough, unfortunately there are some more limitations. One of is that .call and .bind will not work as expected. Currently they will simply ignore the |this| argument and use the original object (the method was fetched from) as |this|. I’ve been trying to fix this bug for a while but it is not that simple to resolve for various reasons.

ChromeObjectWrapper

So in short, if a function object is exposed to content, it can be called. Anything other property access is blocked and an exception is thrown. Alright. There are exceptions of course. The length property of exposed arrays is available as well as the array operator [] on them. And if we do want to expose other properties on purpose, we can define a white list of properties via the __exposedProps__ property on chrome side. Like exportedObject.__exposedProps__ = { foo: “r” }; where foo is the name of the property we let the content access and “r” means read only, “w” write only, and “rw” read write access.

Yes, this means unfortunately that __proto__ does not work either, which means instanceof will not work either… Or if the exported object is a method, exportedMethod.call / .bind will fail and even .toString()… Enabling access to bind and call of the chrome compartment, would be a free ticket to evaluate content code with chrome privileges. There is a workaround…. it is quite far from a perfect solution for the problem and probably this part should be improved in the future, but here it is.http://mxr.mozilla.org/mozilla-central/source/js/xpconnect/tests/chrome/test_APIExposer.xul#28

SafeJSObjectWrapper

No real restrictions.

Cross-OriginWrapper (XOW)

This one is well… interesting. Simply put, it is a hardcoded white list of properties. So calling an exported method is allowed and anything else that is not from this white list is blocked.

object = DOMException

read/write:  “code”, “message”, “name”, “result”

read only: “toString”

object =  Error

read only: “message”

object = History

read only “back”, “forward”, “go”

object = Location

write only: “hash”, “href”

read only: “replace”

object = Window

read only: “blur”, “close”, “closed”, “focus”, “frames”, “history”, “length”, “opener”, “parent”, “postMessage”, “self”, “top”, “window”

read/write: “location”

So for example, if we have a reference to a window from another domain, we can have read access to its postMessage method, and since postMethod is a method, we can call it. Furthermore,  this wrapper also provides XrayWrapper behavior for the allowed operations.

In the same origin case it is simply transparent.

SystemOnlyWrapper:

So if we are some chrome code or some system call, or some code loaded from chrome://global/ (XBL stuff), or the script has UniversalXPConnect privileges (usually some old tests) then the property access is allowed, any other case exception is thrown.

Categories: Uncategorized

No responses {+}

Post Your Comment