W3C home > Mailing lists > Public > public-web-security@w3.org > February 2011

A perfect DOM sandbox

From: gaz Heyes <gazheyes@gmail.com>
Date: Mon, 14 Feb 2011 12:13:40 +0000
Message-ID: <AANLkTinmcqNqHjfDLRoLnuLW0AZps=92j4G3BGg+aKWO@mail.gmail.com>
To: public-web-security@w3.org
Hey all,

Here are my thoughts on native browser sandboxing and it's benefits

In order to create a perfect DOM sandbox you need the ability to read/set
what the browser does. Server side filtering is always doomed to failure
because the various browsers all have different rendering engines. The
server cannot know all these quirks or custom functionality that the browser
adds. It might change from month to month.

If each browser had the ability to parse content without rendering we could
use this to create a perfect sandbox. To filter incoming data the browser
would have a special function to parse the data but not render like the
following:-

parseHTML('<b
style=color:#fff;-some-crazy-vendor-functionality:pwnd>test</b>');

The parseHTML function would return a object/serialized data that the
sandbox could use to read the incoming HTML. We currently have the DOM to do
this dynamically but it is broken in many places on every browser, the main
problem is that the content is not a true representation of the rendering
code. Therefore one CSS rule may become two css rules, escaped HTML may
become malicious HTML when read by innerHTML and so on.

Once we have gathered our whitelist of data we wish to allow, the browser
also needs the ability to set the data, this could be done by using
renderHTML another native browser function.

renderHTML([{tag:'b',style:{color:'#ccc;no new rule'}}]);

Here we are defining a whitelist to render, the renderer should not render
anything other than a bold tag and one css color rule. The colour should
either be assigned #ccc with the invalid rule dropped or the rule should be
dropped entirely because it contains multiple rules.

The same method could be applied to CSS using parseCSS()/renderCSS() and
JavaScript by using parseJavaScript()/executeJavaScript().

I have created a imperfect DOM sandbox using these methods, it's imperfect
because I use the browser DOM to render/read HTML. Originally I tried to use
the DOM to set the HTML but I found many problems in different browsers with
inconsistances in HTML and CSS. So I had to create output that the DOM would
render correctly without modifying into malicious content.

<http://www.businessinfo.co.uk/labs/HTMLReg/HTMLReg.html>

You might have noticed two things with the demo, I modify classes/ids of DOM
objects. This brings me to my next point, you cannot trust DOM objects to be
rendered with classes or id attributes as they conflict with other DOM
objects or native JavaScript objects. We'd also need the ability to define a
prefix/suffix of each class or id used. The developer could invent their own
method but they are likely to invent a bad method or fail to account for
pitfalls such as underscores not being valid CSS classes in some browsers
etc. it would be nice to be able to set a global prefix/suffix that the
rendering functions use to automatically do this.

The second point is my demo proxies image requests, what I mean by that is
that any images specified go through the gmodules proxy. This prevents
sandboxed content from performing CSRF on other sites when a "harmless" img
tag is rendered. We also need the ability to do this natively in the
browser. I thought maybe a image renderer protocol handler or other cool
suggestions at the OWASP summit was to use a "cookie" attribute to disable
cookies from the http request of the image. This would allow a sandbox to
render images and other tags without cookies being sent:-

<img src="//somesite/account?funds=1000&transfer=true" cookies="false">

Integrating a JavaScript sandbox into user supplied HTML could also be done
in the browser take the following examples:-
parsedHTML=parseHTML('<a href="#"
onclick="top.location=\'//evilserver\';alert(1);">test</a>');
//1st arg js, 2nd arg object/function whitelist
parsedOnclick=parseJavaScript(parsedHTML.onclick, {alert:true});//only allow
alert
renderHTML([{tag:'a',href:'#',onclick:parsedOnclick]);


We need these native sandboxing/parsing functions to provide a way to
guarantee output. As soon as HTML is stored somewhere and rendered directly
the filtering performed by the server has already expired. The only true way
to filter content is to provide filtering at the client.

Cheers

Gareth
Received on Monday, 14 February 2011 12:14:14 GMT

This archive was generated by hypermail 2.2.0+W3C-0.50 : Monday, 14 February 2011 12:14:15 GMT