Safely accessing content DOM from chrome

  • Revision slug: Safely_accessing_content_DOM_from_chrome
  • Revision title: Safely accessing content DOM from chrome
  • Revision id: 98883
  • Created:
  • Creator: Jesse
  • Is current revision? No
  • Comment /* What NOT to do */

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 two ways that chrome code generally access the content's DOM, direct access and explicit use of XPCNativeWrapper. The following table summarizes their security properties.

Direct access Explicit XPCNativeWrapper
Firefox 1.0.2 and below insecure secure
Firefox 1.0.3 to 1.0.4 secure when property guaranteed to exist secure
Firefox 1.1 secure with xpcnativewrappers=true secure

1. Direct access is the easier method. Direct access is insecure in Firefox 1.0.2, but that version of Firefox has secure holes of its own. It is secure in Firefox 1.0.3 as long as the object is guaranteed to have the property or method that is accessed through its IDL declaration. For example, foo.nodeType is secure as long as you are sure foo is a Node, and foo.getSelection() is secure as long as you are sure foo is a (...). In Firefox 1.1, direct access is always secure as long as your extension uses the new xpcnativewrappers=yes flag in its manifest because the use of XPCNativeWrapper is implicit.

2. Explicit use of XPCNativeWrapper. Use of XPCNativeWrapper is secure in all versions, but you have to be careful to wrap every DOM object.

What breaks when you use XPCNativeWrapper (explicitly in any version, or with xpcnativewrappers=yes in Firefox 1.1)?

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);

Tricks of this form are insecure in Firefox 1.0.2 and below, prevent the code from working in Firefox 1.0.3 and above on the 1.0.x branch, and are again insecure in Firefox 1.1. If you have used this trick, you must revise your code to use XPCNativeWrapper instead.

About XPCNativeWrapper

XPCNativeWrapper is a way to wrap up an object so that it's safe to access from privileged code.

There are two ways to use XPCNativeWrapper. The old way is described in the entry for XPCNativeWrapper at the MozillaZine KnowledgeBase. The new way is only available starting with Firefox 1.1.

Examples

What NOT to do

BAD in older versions, since script can override nodeType getter:

return targetNode.nodeType == 1;

BAD in older versions, since script can override getSelection:

return contentWindow.getSelection();

BAD in all versions. Some developers have, in the past, used this ill-advised trick. Script can override getSelection in older versions and this doesn't work at all in Firefox 1.0.3 and Mozilla 1.7.7:

return contentWindow.__proto__.getSelection.call(contentWindow);

BAD in older versions, since script can override inner getter even though outer one is safe:

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

BAD in versions before Firefox 1.1, since script can set document.title for non-HTML documents, which don't have a DOM document.title:

  return contentWindow.document.title;

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 applies only to scripts for Firefox 1.0.2 and earlier. Scripts designed to run only in Firefox 1.0.3 and later 1.0 versions or that use xpcnativewrappers=yes in Firefox 1.1 or later may simply call:

return contentWindow.document.documentElement == 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 two ways that chrome code generally access the content's
DOM, direct access and explicit use of XPCNativeWrapper.  The following table summarizes their security properties.
</p>
<table border="1">
<tbody><tr>
<th>
</th><th> Direct access
</th><th> Explicit XPCNativeWrapper
</th></tr>
<tr>
<th> Firefox 1.0.2 and below
</th><td> insecure
</td><td> secure
</td></tr>
<tr>
<th> Firefox 1.0.3 to 1.0.4
</th><td> secure when property guaranteed to exist
</td><td> secure
</td></tr>
<tr>
<th> Firefox 1.1
</th><td> secure with xpcnativewrappers=true
</td><td> secure
</td></tr></tbody></table>
<p>1. Direct access is the easier method.  Direct access is insecure in Firefox 1.0.2, but that version of Firefox has secure holes of its own.  It is secure in Firefox 1.0.3 as long as the object is guaranteed to have the property or method that is accessed through its IDL declaration.  For example, foo.nodeType is secure as long as you are sure foo is a Node, and foo.getSelection() is secure as long as you are sure foo is a (...).  In Firefox 1.1, direct access is always secure as long as your extension uses the new <code>xpcnativewrappers=yes</code> flag in <a href="en/Chrome_Registration">its manifest</a> because the use of <a href="#About_XPCNativeWrapper">XPCNativeWrapper</a> is implicit.
</p><p>2. Explicit use of <a href="#About_XPCNativeWrapper">XPCNativeWrapper</a>. Use of XPCNativeWrapper is secure in all versions, but you have to be careful to wrap every DOM object.
</p><p>What breaks when you use XPCNativeWrapper (explicitly in any version, or with xpcnativewrappers=yes in Firefox 1.1)?
</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>Tricks of this form are insecure in Firefox 1.0.2 and below, prevent the code from working in Firefox 1.0.3 and above on the 1.0.x branch, and are again insecure in Firefox 1.1.  If you have used this trick, you must revise your code to use <code>XPCNativeWrapper</code> instead.
</p>
<h3 name="About_XPCNativeWrapper"> About XPCNativeWrapper </h3>
<p><code><a href="en/XPCNativeWrapper">XPCNativeWrapper</a></code> is a way to wrap up an object so that it's safe to access from privileged code.
</p><p>There are two ways to use <code>XPCNativeWrapper</code>.  The old way is described in <a class="external" href="http://kb.mozillazine.org/XPCNativeWrapper">the entry for <code>XPCNativeWrapper</code> at the MozillaZine KnowledgeBase</a>.  The <a href="en/XPCNativeWrapper">new way</a> is only available starting with Firefox 1.1.
</p>
<h3 name="Examples"> Examples </h3>
<h4 name="What_NOT_to_do"> What NOT to do </h4>
<p>BAD in older versions, since script can override <code>nodeType</code> getter:
</p>
<pre>return targetNode.nodeType == 1;
</pre>
<p>BAD in older versions, since script can override <code>getSelection</code>:
</p>
<pre>return contentWindow.getSelection();
</pre>
<p>BAD in <em>all</em> versions. Some developers have, in the past, used this ill-advised trick. Script can override <code>getSelection</code> in older versions <em>and</em> this doesn't work at all in Firefox 1.0.3 and Mozilla 1.7.7:
</p>
<pre>return contentWindow.__proto__.getSelection.call(contentWindow);
</pre>
<p>BAD in older versions, since script can override inner getter even though outer one is safe:
</p>
<pre>var winWrapper = new XPCNativeWrapper(contentWindow, 'document');
// getting contentWindow.document is now safe, but getting .title off
// the returned document is still not safe.
return winWrapper.document.documentElement;
</pre>
<p>BAD in versions before Firefox 1.1, since script can set document.title for non-HTML documents, which don't have a DOM document.title:
</p>
<pre>  return contentWindow.document.title;
</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 applies only to scripts for Firefox 1.0.2 and earlier.  Scripts
designed to run only in Firefox 1.0.3 and later 1.0 versions or that use <code>xpcnativewrappers=yes</code> in Firefox 1.1 or later may simply call:
</p>
<pre>return contentWindow.document.documentElement == 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