Safely accessing content DOM from chrome

  • Revision slug: Safely_accessing_content_DOM_from_chrome
  • Revision title: Safely accessing content DOM from chrome
  • Revision id: 98874
  • Created:
  • Creator: Jesse
  • Is current revision? No
  • Comment /* Introduction */

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:

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, secure in Firefox 1.0.3+ and Mozilla 1.7.7+ as long as the object is guaranteed to have the property or method, and secure in Firefox 1.1 as long as the extension is using the new xpcnativewrappers=yes flag in its manifest. For this last case, please read about XPCNativeWrapper.

2. Explicit use of 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.

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 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, since 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 all versions, 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 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:
</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, secure in Firefox
1.0.3+ and Mozilla 1.7.7+ as long as the object is guaranteed to
have the property or method, and secure in Firefox 1.1 as long as the extension is using the new <code>xpcnativewrappers=yes</code> flag in <a href="en/Chrome_Registration">its manifest</a>.  For this last case, please read about <a href="#About_XPCNativeWrapper">XPCNativeWrapper</a>.
</p><p>2. Explicit use of <a href="#About_XPCNativeWrapper">XPCNativeWrapper</a>: 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>
<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 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, since 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 <em>all</em> versions, 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 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