Safely accessing content DOM from chrome

  • Revision slug: Safely_accessing_content_DOM_from_chrome
  • Revision title: Safely accessing content DOM from chrome
  • Revision id: 98866
  • Created:
  • Creator: DBaron
  • Is current revision? No
  • Comment /* Do not use the <code>__proto__</code> trick */ change "hack" to "trick"

Revision Content

Introduction

Application and extensions that have Javascript code using DOM interfaces on untrusted (Web) content need to be careful that the information that they use is really coming from the DOM API and not from JS properties defined by a malicious page. Firefox 1.0.3 and Mozilla 1.7.7 make it harder for Web pages to trick XUL applications and extensions by ensuring that when chrome Javascript accesses a DOM property or method on an object, it will get the DOM property or method rather than the Web page's override.

There are three ways that chrome code generally access the content's DOM:

1. Direct access (e.g., node.nodeType or focusedWindow.getSelection()): Application and extension code that accesses content DOM properties and methods directly is insecure in older versions, but secure in Firefox 1.0.3 and Mozilla 1.7.7 as long as the object node is guaranteed to have the DOM property nodeType or the object focusedWindow is guaranteed to have a DOM method getSelection.

2. XPCNativeWrapper: Use of XPCNativeWrapper is secure in all versions and is the recommended method, particularly when it's important to avoid content-defined properties that do not shadow a DOM property of the same name.

3. focusedWindow.__proto__.getSelection.call(focusedWindow): This pattern is insecure in older versions and does not work at all in Firefox 1.0.3 and Mozilla 1.7.7. It must be changed to one of the previous two methods in order to work in these versions.

Do not use the __proto__ trick

Some developers have, in the past, used an ill-advised trick to avoid the name collisions mentioned here. This trick took the following form:

selection = focusedWindow.__proto__.getSelection.call(focusedWindow);

Extension authors should not be calling content.__proto__.f.call for any function f; likewise for content DOM objects other than the window named in chrome by content or _content (its old name).

If you have used this trick, you must now revise your code to use XPCNativeWrapper instead. Tricks of this form will no longer work in Firefox 1.0.3 or later.

About XPCNativeWrapper

XPCNativeWrapper is a JavaScript object that should be used whenever privileged code is used to access unprivileged code.

Using XPCNativeWrapper is simple: an instance of XPCNativeWrapper is created with the untrusted object as the first parameter and the desired methods of the untrusted object as additional parameters.

For more information see the entry for XPCNativeWrapper at the MozillaZine KnowledgeBase.

Examples

What NOT to do

BAD (script can override getters):

return contentWindow.document.title == contentWindow.getSelection();

BAD (script can override getters):

return contentWindow.document.title ==
       contentWindow.__proto__.getSelection.call(contentWindow);

BAD (script can override getters):

var winWrapper = new XPCNativeWrapper(contentWindow,
                                      'document', getSelection()');
// getting contentWindow.document is now safe, but getting .title off
// the returned document is still not safe.
return winWrapper.document.title == winWrapper.getSelection();

What TO do

GOOD (gets the real, native getters):

var winWrapper = new XPCNativeWrapper(contentWindow,
                                      'document', 'getSelection()');
var docWrapper = new XPCNativeWrapper(winWrapper.document, 'title');
return docWrapper.title == winWrapper.getSelection();

Note that this example uses two wrappers to get window.document.title, one wrapper for getting the document property from the window, and one wrapper for getting the title property from the document.

The above only applies to scripts for Firefox 1.0.2 and earlier. Scripts designed to run only in Firefox 1.0.3 and later may simply call:

return contentWindow.document.title == contentWindow.getSelection();

Scripts that are designed to run in both Firefox 1.0.2 and earlier and Firefox 1.0.3 and later must use the XPCNativeWrapper version of the script.

Revision Source

<h3 name="Introduction"> Introduction </h3>
<p>Application and extensions that have Javascript code using DOM
interfaces on untrusted (Web) content need to be careful that the
information that they use is really coming from the DOM API and not from
JS properties defined by a malicious page.  Firefox 1.0.3 and Mozilla
1.7.7 make it harder for Web pages to trick XUL applications and
extensions by ensuring that when chrome Javascript accesses
a DOM property or method on an object, it will get the DOM
property or method rather than the Web page's override.
</p><p>There are three ways that chrome code generally access the content's
DOM:
</p><p>1. Direct access (e.g., node.nodeType or focusedWindow.getSelection()):
Application and extension code that accesses content DOM properties
and methods directly is insecure in older versions, but secure in Firefox
1.0.3 and Mozilla 1.7.7 as long as the object node is guaranteed to
have the DOM property nodeType or the object focusedWindow is
guaranteed to have a DOM method getSelection.
</p><p>2. XPCNativeWrapper: Use of XPCNativeWrapper is secure in all versions and is the
recommended method, particularly when it's important to avoid content-defined properties that do not shadow a DOM property of the same name.
</p><p>3. focusedWindow.__proto__.getSelection.call(focusedWindow): This pattern is insecure in older versions and does not work at all
in Firefox 1.0.3 and Mozilla 1.7.7.  It must be changed to one of
the previous two methods in order to work in these versions.
</p>
<h3 name="Do_not_use_the___proto___trick"> Do not use the <code>__proto__</code> trick </h3>
<p>Some developers have, in the past, used an ill-advised trick to avoid the name collisions mentioned here.  This trick took the following form:
</p>
<pre>selection = focusedWindow.__proto__.getSelection.call(focusedWindow);
</pre>
<p>Extension authors should not be calling <code>content.__proto__.f.call</code> for any function <code>f</code>; likewise for content DOM objects other than the window named in chrome by <code>content</code> or <code>_content</code> (its old name).
</p><p>If you have used this trick, you must now revise your code to use <code>XPCNativeWrapper</code> instead.  Tricks of this form will no longer work in Firefox 1.0.3 or later.
</p>
<h3 name="About_XPCNativeWrapper"> About XPCNativeWrapper </h3>
<p><code>XPCNativeWrapper</code> is a JavaScript object that should be used whenever privileged code is used to access unprivileged code.  
</p><p>Using <code>XPCNativeWrapper</code> is simple: an instance of <code>XPCNativeWrapper</code> is created with the untrusted object as the first parameter and the desired methods of the untrusted object as additional parameters.  
</p><p>For more information see the <a class="external" href="http://kb.mozillazine.org/XPCNativeWrapper">entry for <code>XPCNativeWrapper</code></a> at the MozillaZine KnowledgeBase.
</p>
<h3 name="Examples"> Examples </h3>
<h4 name="What_NOT_to_do"> What NOT to do </h4>
<p>BAD (script can override getters):
</p>
<pre>return contentWindow.document.title == contentWindow.getSelection();
</pre>
<p>BAD (script can override getters):
</p>
<pre>return contentWindow.document.title ==
       contentWindow.__proto__.getSelection.call(contentWindow);
</pre>
<p>BAD (script can override getters):
</p>
<pre>var winWrapper = new XPCNativeWrapper(contentWindow,
                                      'document', getSelection()');
// getting contentWindow.document is now safe, but getting .title off
// the returned document is still not safe.
return winWrapper.document.title == winWrapper.getSelection();
</pre>
<h4 name="What_TO_do"> What TO do </h4>
<p>GOOD (gets the real, native getters):
</p>
<pre>var winWrapper = new XPCNativeWrapper(contentWindow,
                                      'document', 'getSelection()');
var docWrapper = new XPCNativeWrapper(winWrapper.document, 'title');
return docWrapper.title == winWrapper.getSelection();
</pre>
<p>Note that this example uses <em>two</em> wrappers to get window.document.title, one wrapper for getting the document property from the window, and one wrapper for getting the title property from the document.
</p><p>The above only applies to scripts for Firefox 1.0.2 and earlier.  Scripts
designed to run only in Firefox 1.0.3 and later may simply call:
</p>
<pre>return contentWindow.document.title == contentWindow.getSelection();
</pre>
<p>Scripts that are designed to run in <i>both</i> Firefox 1.0.2 and earlier and Firefox 1.0.3 and later must use the <code>XPCNativeWrapper</code> version of the script.
</p>
Revert to this revision