Just as a check however, let me assert that the section "Accessing
unsafe properties" on https://developer.mozilla.org/en/XPCNativeWrapper
is incorrect. It starts
---
If unsafe access to a property is required for some reason, this can
be accomplished via the wrappedJSObject property of the wrapper.
---
I believe that in recent versions of Firefox, this statement is
incorrect and misleading, right? It should read:
---
To obtain a property value set by a web page, access the properties
of the wrappedJSObject in your wrapper.
---
(The rest of the section is also confused).
Now to the main event: the doc should continue:
---
... You may call functions in the web page through the
wrappedJSObject however they will run with the web page privileges. In
the case of simple functions like getters and setters, this will
probably give you results you expect. In more complex cases, functions
in the page should be invoked using event handlers in the page to ensure
predictable outcomes.
---
Is this correct?
Here I am trying to decode the Note in
https://developer.mozilla.org/en/XPConnect_wrappers#XPCSafeJSObjectWrapper
that says
---
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.
---
jjb
Maybe a more compact question will help:
Is there any unsafe operation an extension can to on
<webpage-object>.wrappedJSObject?
jjb
Ok I guess we'll just waited to see if users get powned.
jjb
Well... the short obvious answer to the above question is "of course,
yes!". For example:
eval(<webpage-object>.wrappedJSObject.foo);
is totally unsafe.
So it seems like there must be more to your question, right? If so, what?
-Boris
Ok, thanks, that's progress! Here's how I understand it: Since
<webpage-object>.wrappedJSObject.foo could result in a string and since
eval(string) in a extension call stack can perform privileged
operations, eval(<webpage-object>.wrappedJSObject.foo) is unsafe. Same
would apply to setTimeout() and new Function().
I assume that
var x = <webpage-object>.wrappedJSObject.foo;
is safe at all times.
>
> So it seems like there must be more to your question, right? If so, what?
I want to understand what is safe and what is unsafe so we can check our
code. Is it too much to ask?
jjb
>
> -Boris
Yes, exactly.
> I assume that
> var x = <webpage-object>.wrappedJSObject.foo;
> is safe at all times.
Yes. At least if it's not, we have a _really_ serious bug. The whole
point of XPCSafeJSObjectWrapper (which is what .wrappedJSObject hands
back there) is to make the above safe.
> I want to understand what is safe and what is unsafe so we can check our
> code. Is it too much to ask?
Given the open-ended nature of the question, and the variety of APIs
available to you here... possibly.
The security guarantees that are provided are that:
1) Getting a property from .wrappedJSObject is safe in the sense that
the get _might_ run script of the page's choosing but will do so
with the page's privileges. Further, the value returned by the get
will also offer this security guarantee.
2) Setting a property on .wrappedJSObject is safe in the sense that
the set might run script of the page's choosing and the value you
are setting will be available to the script, but the script will
run with tge page's privileges.
Everything else really depends on whatever it is that you're doing, not
on the properties of .wrappedJSObject itelf....
-Boris
But it's not an open ended question. The set of available operations on
properties of .wrappedJSObject is fixed by the runtime. (We exclude
operations by plugins and other extensions, that is between them and
their users). We need to know which of those operations are safe and
which are not safe. Otherwise we don't know if our extension is safe or
unsafe for users.
>
> The security guarantees that are provided are that:
>
> 1) Getting a property from .wrappedJSObject is safe in the sense that
> the get _might_ run script of the page's choosing but will do so
> with the page's privileges. Further, the value returned by the get
> will also offer this security guarantee.
> 2) Setting a property on .wrappedJSObject is safe in the sense that
> the set might run script of the page's choosing and the value you
> are setting will be available to the script, but the script will
> run with tge page's privileges.
And this guarantee is also recursive? That is, any value from #1 is also
safe to set properties on?
Is any kind of property setting is subsequently safe? Specifically is it
safe to set a function object created in extension scope into a property
of on .wrappedJSObject? Can it run with more than page privileges?
>
> Everything else really depends on whatever it is that you're doing, not
> on the properties of .wrappedJSObject itelf....
But so far I don't see why you are pessimistic. So far the only unsafe
case is where we compile a string obtained from a web page. If the
string is compiled in the web page, there is nothing it can do to us.
jjb
It's not, because you can always find yet another XPCOM method to pass
the stuff to...
> We need to know which of those operations are safe and
> which are not safe.
So what's the list of operations involved as you see it?
>> 2) Setting a property on .wrappedJSObject is safe in the sense that
>> the set might run script of the page's choosing and the value you
>> are setting will be available to the script, but the script will
>> run with tge page's privileges.
>
> And this guarantee is also recursive? That is, any value from #1 is also
> safe to set properties on?
You mean the value returned by the setter? Yes.
> Is any kind of property setting is subsequently safe? Specifically is it
> safe to set a function object created in extension scope into a property
> of on .wrappedJSObject?
Safe in the sense that doing so is not immediately exploitable, I thinks
yes. Whether it's exploitable longer-term depends on what that function
does.
> Can it run with more than page privileges?
No. It either can't be called from content at all, or will run with
chrome privileges if called; not sure which it is in this case.
>> Everything else really depends on whatever it is that you're doing, not
>> on the properties of .wrappedJSObject itelf....
>
> But so far I don't see why you are pessimistic.
Uh... pessimistic in what sense?
> So far the only unsafe case is where we compile a string obtained from a web page.
Or set it as the value of certain attributes in a chrome context. Or
pass it to someone who might happen to do any of those things. That's
off the top of my head.
> If the string is compiled in the web page, there is nothing it can do to us.
Yes.
-Boris
Let's see. Global functions of browser.xul. Global functions and methods
of global objects in Javascript. Functions of any XPCOM service shipped
with Firefox. Intrinsic operators of JavaScript. I guess that is the list.
>
>>> 2) Setting a property on .wrappedJSObject is safe in the sense that
>>> the set might run script of the page's choosing and the value you
>>> are setting will be available to the script, but the script will
>>> run with tge page's privileges.
>>
>> And this guarantee is also recursive? That is, any value from #1 is also
>> safe to set properties on?
>
> You mean the value returned by the setter? Yes.
Actually I meant set on any object returned by get, but I think you
answered that as "safe" already.
>
>> Is any kind of property setting is subsequently safe? Specifically is it
>> safe to set a function object created in extension scope into a property
>> of on .wrappedJSObject?
>
> Safe in the sense that doing so is not immediately exploitable, I thinks
> yes. Whether it's exploitable longer-term depends on what that function
> does.
>
>> Can it run with more than page privileges?
>
> No. It either can't be called from content at all, or will run with
> chrome privileges if called; not sure which it is in this case.
Well that is what we need to know. Look at this problem from our
perspective. We are going to do some operation:
foo = bar;
If foo ultimately comes from wrappedJSObject, then we really know that
bar is not a function and has no function properties and no getters. Or
not. Same goes for jetpack or any other extn.
>
>>> Everything else really depends on whatever it is that you're doing, not
>>> on the properties of .wrappedJSObject itelf....
>>
>> But so far I don't see why you are pessimistic.
>
> Uh... pessimistic in what sense?
In the sense that this security analysis is hopeless. I'm trying to
claim it's only uninformed.
To be clear I'm not asking for completeness with respect to analyzing
our code or any code outside of the platform+Firefox.
>
>> So far the only unsafe case is where we compile a string obtained from
>> a web page.
>
> Or set it as the value of certain attributes in a chrome context. Or
...of certain attributes of certain elements in a chrome content?
> pass it to someone who might happen to do any of those things. That's
> off the top of my head.
But I want the whole head ;-)
Or with arbitrary extensions that override those services, right?
> Intrinsic operators of JavaScript. I guess that is the list.
And my point is that while this list is finite, it's large and we have
no idea what's on it. Worse yet, the list of things that might be on it
tomorrow (arbitrary XPCOM service stuff) is almost certainly infinite.
>> You mean the value returned by the setter? Yes.
>
> Actually I meant set on any object returned by get, but I think you
> answered that as "safe" already.
Ah, I'm not sure I did. But yes, it's safe; the return value of a
getter offers the same security guarantees as the .wrappedJSObject.
> Well that is what we need to know. Look at this problem from our
> perspective. We are going to do some operation:
> foo = bar;
> If foo ultimately comes from wrappedJSObject, then we really know that
> bar is not a function and has no function properties and no getters. Or
> not. Same goes for jetpack or any other extn.
I'm not sure I follow the above.
>> Uh... pessimistic in what sense?
>
> In the sense that this security analysis is hopeless. I'm trying to
> claim it's only uninformed.
No, I think in the scope you stated at the beginning of the mail above
it's in fact hopeless.
> To be clear I'm not asking for completeness with respect to analyzing
> our code or any code outside of the platform+Firefox.
And I'm just saying that this is already a sufficiently large list of
things to analyze that it would take man-years to do it.
>> Or set it as the value of certain attributes in a chrome context. Or
>
> ....of certain attributes of certain elements in a chrome content?
Pretty much any element. Think of this as the "don't stick random
strings you don't control into the onclick attribute" rule.
>> pass it to someone who might happen to do any of those things. That's
>> off the top of my head.
>
> But I want the whole head ;-)
And I don't have the time to analyze all possible API entrypoints....
-Boris
No, I am specifically excluding plugins and extensions: I want to be
responsible for the things we can change, not the things we cannot. The
potential for another extension to introduce a security hole does not
absolve us from trying to close ours.
>
> > Intrinsic operators of JavaScript. I guess that is the list.
>
> And my point is that while this list is finite, it's large and we have
> no idea what's on it. Worse yet, the list of things that might be on it
> tomorrow (arbitrary XPCOM service stuff) is almost certainly infinite.
>
>>> You mean the value returned by the setter? Yes.
>>
>> Actually I meant set on any object returned by get, but I think you
>> answered that as "safe" already.
>
> Ah, I'm not sure I did. But yes, it's safe; the return value of a
> getter offers the same security guarantees as the .wrappedJSObject.
>
>> Well that is what we need to know. Look at this problem from our
>> perspective. We are going to do some operation:
>> foo = bar;
>> If foo ultimately comes from wrappedJSObject, then we really know that
>> bar is not a function and has no function properties and no getters. Or
>> not. Same goes for jetpack or any other extn.
>
> I'm not sure I follow the above.
I need to analyze our code. If I see
foo = bar;
where I know that foo is a obtained as a property (of a property...) of
wrappedJSObject, what shall I do:
1) remove that code, it is unsafe,
2) analyze bar, but how??
3) no worries, the platform secures it.
Is that clearer?
>
>>> Uh... pessimistic in what sense?
>>
>> In the sense that this security analysis is hopeless. I'm trying to
>> claim it's only uninformed.
>
> No, I think in the scope you stated at the beginning of the mail above
> it's in fact hopeless.
>
>> To be clear I'm not asking for completeness with respect to analyzing
>> our code or any code outside of the platform+Firefox.
>
> And I'm just saying that this is already a sufficiently large list of
> things to analyze that it would take man-years to do it.
So what do we do then? Wait and see if users get exploited?
jjb
The point is that an extension can change the behavior of a service in a
way that is safe on its own in general but happens to be unsafe in
combination with the specific way you're using the service. It might
not even have any way to tell that your use will cause a problem.
>> I'm not sure I follow the above.
>
> I need to analyze our code. If I see
> foo = bar;
> where I know that foo is a obtained as a property (of a property...) of
> wrappedJSObject
This is the part I don't get. Is the actual code of the form |foo =
bar;| (i.e. with a bareword on the LHS)? If so, what does it mean that
"foo is obtained as a property" of anything? If not, what form does the
code actually have?
> 1) remove that code, it is unsafe,
> 2) analyze bar, but how??
> 3) no worries, the platform secures it.
> Is that clearer?
Not quite; see above.
> So what do we do then? Wait and see if users get exploited?
Well, if your list of possible things you actually _do_ as opposed to
might do, is short enough we could analyze each of them. If it's very
long, then you have a fundamental problem, yes.... It might be solvable
via fuzz testing, or restricting what things are done, or hiring a few
people for a year or two to analyze all the possibilities. :(
-Boris
Does that mean we should not bother with any security issues? If not
amount of work on our part will prevent security issues, then we may as
well not bother? I don't think that is what you mean. So I guess we
just have to live with this exotic possibility, do our best, and assume
this case won't come up in practice.
>
>>> I'm not sure I follow the above.
>>
>> I need to analyze our code. If I see
>> foo = bar;
>> where I know that foo is a obtained as a property (of a property...) of
>> wrappedJSObject
>
> This is the part I don't get. Is the actual code of the form |foo =
> bar;| (i.e. with a bareword on the LHS)? If so, what does it mean that
> "foo is obtained as a property" of anything? If not, what form does the
> code actually have?
>
>> 1) remove that code, it is unsafe,
>> 2) analyze bar, but how??
>> 3) no worries, the platform secures it.
>> Is that clearer?
>
> Not quite; see above.
Does this mean that the only option is:
4) post the code and ask Boris
? That seems very inefficient and error prone, especially when we have
lots of releases.
>
>> So what do we do then? Wait and see if users get exploited?
>
> Well, if your list of possible things you actually _do_ as opposed to
> might do, is short enough we could analyze each of them. If it's very
> long, then you have a fundamental problem, yes.... It might be solvable
> via fuzz testing, or restricting what things are done, or hiring a few
> people for a year or two to analyze all the possibilities. :(
I'm perfectly fine with "restricting what things are done", but we don't
know what those restrictions are. Even an overly limiting list of things
that are ok plus "you may ask about xxx" plus "don't do yyy" would be
great.
jjb
The right way to handle security issues is:
1) Reduce attack surface as much as possible (in this case, reduce
the number of things you do with untrusted objects as much as
possible).
2) Secure the remaining attack surface as well as you can (in this
case, make sure the things you're still doing are safe).
In particular, the right way to handle issues with services being
replaced is to not pass tainted data into those services if that can be
avoided. What that means in practice depends on the service.
>> This is the part I don't get. Is the actual code of the form |foo =
>> bar;| (i.e. with a bareword on the LHS)? If so, what does it mean that
>> "foo is obtained as a property" of anything? If not, what form does the
>> code actually have?
>>
>>> 1) remove that code, it is unsafe,
>>> 2) analyze bar, but how??
>>> 3) no worries, the platform secures it.
>>> Is that clearer?
>>
>> Not quite; see above.
>
> Does this mean that the only option is:
> 4) post the code and ask Boris
> ? That seems very inefficient and error prone, especially when we have
> lots of releases.
Well, look. You're trying to tell whether some code is safe. Your
options are either to understand the system well enough to tell whether
it is (and knowledge of the code is probably needed to decide what parts
of the system are relevant), or to show the code to someone else and ask
them to look. It doesn't have to be "Boris".
This is why we have code reviews. Sometimes by multiple people if
domain expertise is needed. And we still end up with security bugs
sometimes; it turns out security is _hard_, and proving something secure
in this sort of environment is very difficult.
Now I'm happy to help you along on the "understand the system" bit,
which is what you seem to be asking. But the right way to do that is
not to ask "what's the set of all things that could possibly be done,
and are they safe" but to either understand the system's invariants
(we've started in on that) or examine simple cases and build up
understanding from there (what I suggested we do here). Or better yet a
combination of both.
> I'm perfectly fine with "restricting what things are done", but we don't
> know what those restrictions are. Even an overly limiting list of things
> that are ok plus "you may ask about xxx" plus "don't do yyy" would be
> great.
1) You can read properties from content objects, and the act of reading
them is safe. The result also satisfies this property.
2) You can safely set properties on content objects to primitive values.
3) You can safely set properties on content objects to object values,
modulo rule 5. All objects/functions reachable via the object value
would be visible to content, I think.
4) You don't want to pass anything coming from content to any place that
treats strings as JS source.
5) You don't want to allow content to directly call chrome-privilege
functions unless they have been _very_ carefully vetted and you
understand completely all places that content-controlled data can reach
via those functions.
OK. What things not on that list are of interest?
-Boris
>
> jjb
Excellent! This is exactly the kind of thing I was looking for! Well
maybe not exactly...
>
> 1) You can read properties from content objects, and the act of reading
> them is safe. The result also satisfies this property.
>
> 2) You can safely set properties on content objects to primitive values.
>
> 3) You can safely set properties on content objects to object values,
> modulo rule 5. All objects/functions reachable via the object value
> would be visible to content, I think.
>
> 4) You don't want to pass anything coming from content to any place that
> treats strings as JS source.
>
> 5) You don't want to allow content to directly call chrome-privilege
> functions unless they have been _very_ carefully vetted and you
> understand completely all places that content-controlled data can reach
> via those functions.
>
> OK. What things not on that list are of interest?
...in #5 we face the same kind of issue again: we don't know how to vet
the functions we might use in a content page. Again I'm not asking for a
comprehensive list, just a few things we know are safe. I believe that
chrome code called from content can:
1) read content objects,
2) assign content objects to content objects,
3) call DOM platform methods and pass content objects.
4) assign strings obtained from content objects to chrome object
properties,
5) assign strings obtained from chrome objects to content objects
6) avoid passing content objects into chrome functions unless you can
ensure that you don't violate the rest of the guidelines.
7) beware that chrome functions can close over chrome objects.
It seems to me that at lot could be gained here if the chrome code was
compiled in a sandbox with a limited scope.
>
> -Boris
>
>
>>
>> jjb
>
"recursively". ;) Basically assume that the page can pass arbitrary
arguments of its choosing, and make sure your function deals with any
behavior on their part.
> 1) read content objects,
> 2) assign content objects to content objects,
> 3) call DOM platform methods and pass content objects.
Mostly yes, though if you violate same-origin restrictions in #3 there
(which you can do, as chrome) you can lose.
> 4) assign strings obtained from content objects to chrome object
> properties
If you're guaranteed that they're strings, and if you're careful about
what you do with those chrome object properties, yes.
> 5) assign strings obtained from chrome objects to content objects
Yes.
> 6) avoid passing content objects into chrome functions unless you can
> ensure that you don't violate the rest of the guidelines.
> 7) beware that chrome functions can close over chrome objects.
Yes.
You can also make network requests or read the filesystem and combine
resulting strings with strings you get from content....
> It seems to me that at lot could be gained here if the chrome code was
> compiled in a sandbox with a limited scope.
That's possible, yes.
-Boris
jjb
What about evalInSandbox? That seems like it will be more unsafe
according to the docs[1]:
--------------------
var s = new Components.utils.Sandbox(extensionWindowReference);
var x = Components.utils.evalInSandbox("someGlobalVar", s);
if (x == 1) {
/* this code is unsafe; calls x.valueOf() */
}
var y = x.y; /* this is unsafe */
--------------------
So:
x = extensionWindowReference.wrappedJSObject.someGlobalVar;
is far safer, correct?
What if I want to call a function on this global object?
x =
extensionWindowReference.wrappedJSObject.someGlobalVar.someFunction();
Is that unsafe? Should I instead:
var s = new Components.utils.Sandbox(extensionWindowReference);
var x = Components.utils.evalInSandbox("someGlobalVar.someFunction()",
s);
But then x is really unsafe now, so then what? Do I use the
evalInSandbox("x.valueOf()",s) when I need the value?
The sandbox stuff doesn't say anything about objects getting wrappers.
[1] https://developer.mozilla.org/en/Components.utils.evalInSandbox