[whatwg] Sandboxing ideas

A number of people made proposals related to sandboxing content to improve 
Web security.

To be honest, I did not think the proposals by and large were very well 
thought out; they seemed to be based mostly on intuition and not on 
careful design. For example, none of the proposals clearly stated what 
problem they were trying to solve.

Now, in all fairness, this is a complicated issue, with many subtleties 
and with a number of unintuitive aspects.

First I'll reply to the key e-mails received on this topic, and then I'll 
review possible problems we could try to solve and possible solutions to 
those problems.


On Thu, 15 Dec 2005, Hallvord R M Steen wrote:
>
> A script from another server today has no limits if included in a page 
> with a SCRIPT src= tag. In many scenarios it would be interesting to a 
> webmaster to have a sort of "grades of trust" approach and specify some 
> more about what one would allow a foreign script to do, for example but 
> not limited to when including third-party advertising. However, I would 
> rather not see anything approaching the Java security model in terms of 
> complexity.

Indeed, every bit of complexity is another possibility for bugs.


> Here's what I thought: a new attribute "sandbox" (or "securitypolicy", 
> name doesn't matter much) might tell the UA something about what the 
> script can do:
> 
> <script src="..." sandbox="writeonly">
> - script may use ECMAScript features and variables already created in
> page but may not use any host objects or methods except document.open
> / .write / .close. Typical use case is advertisment scripts that only
> add content to document.

What if it document.write()s something evil? e.g. it could document.write 
a static replacement to your login form, with a form that redirects 
through an authentication collection server.

(Note that if you don't trust the company that is putting advertisments on 
your Web page, you have bigger problems than controlling what scripts they 
can run.)


> <script src="..." sandbox="none">
> - script may not alter document at all, only create variables. Typical
> use case is a script that just adds data from a third-party source,
> for example creates arrays or JSON structures.

In many cases, though, adding variables can cause as much damage as just 
writing the data in the first place.


> In all cases the limitation would apply only to the thread created by 
> that SCRIPT tag. Functions defined in those scripts might be called 
> later and would run with normal privileges.

Unfortunately that itself opens the door to a lot of abuse (it's trivial 
to cause future scripts to run your functions in a situation like this).


On Fri, 16 Dec 2005, Alexey Feldgendler wrote:
> 
> 1. The entire thing has to degrade SAFELY in existing browsers. With 
> your approach, any existing browser will just ignore the unknown 
> "sandbox" attribute, effectively allowing the script to do anything. 
> This is not acceptable.

This probably depends on the use cases in question. For some use cases, 
the status quo is in fact the script running with full privileges, so 
while not being ideal, it is indeed acceptable; in other cases, you 
wouldn't want scripts to run at all if they weren't limited in some way.


> The primary danger of JS is that when different kinds of a single HTML 
> page come from different sources (are authored by different persons). As 
> a real life example, I'll take LiveJournal.com, where it's currently 
> completely forbidden to include any scripts in journal entries or 
> comments. They would like to allow some scripting, only if they could 
> somehow separate harmless scripts from potentially dangerous ones.
> 
> The idea is to add a new element, <sandbox> (the actual name doesn't 
> matter much). This element can appear anywhere (in both <head> and 
> <body>) and can include any elements that it's direct parent can 
> include. It has no other effect on its contents than altering the 
> contained scripts' security. Everything enclosed in 
> <sandbox>...</sandbox> is somewhat limited in what scripts can do.
> 
> 1. All scripts inside <sandbox> are affected. This includes <script>, 
> javascript: URIs, "onclick" etc. Other active objects like <object> that 
> can access DOM are affected too (for example, Flash is restricted by 
> <sandbox>, too).
> 
> 2. The script in a <sandbox> thinks that it's god. It can do everything, 
> but only inside that sandbox.
> 
> 2.1. The window.document actually represents the part of document inside 
> <sandbox>. Yes, it's not a valid HTML document -- with fake body element 
> (window.document.body being the document fragment inside <sandbox>), and 
> otherwise looking strange, but it's usable by most scripts. Other DOM 
> facilities also pretend that there's nothing outside the sandbox.
> 
> 2.2. If the <sandbox> has a domain="..." attribute, then the scripts 
> inside the sandbox have access to cookies from the specified domain, can 
> interact with other sandboxes and frames from that domain, and are 
> otherwise restricted in a similar way as a regular content from that 
> domain (but not breaking out of 2.1 restriction). The "domain" attribute 
> can only specify the domain of the containing document or a subdomain 
> thereof. (For example, LiveJournal can specify synthetized subdomains 
> like <user>.livejournal.com for entries by each user.) If there is no 
> "domain" attribute, then the inner JS doesn't have access to any 
> cookies.
> 
> 2.3. The JS namespace in a sandbox is isolated. JS inside the sandbox 
> cannot see the variables and functions declared outside, and vice versa. 
> JS outside the sandbox can accesss JS variables and functions from 
> inside the sandbox in an explicit way (like 
> sandboxElement.sandbox['variable']). If the outer JS needs to make 
> several things (DOM nodes, JS variables) from the outside accessible to 
> the inner JS, it can do so by putting references to these into 
> sandboxElement.sandbox array.
> 
> 2.4. Multiple sandboxes on one page sharing the same "domain" attribute 
> value share the same JS namespace (sandboxElement1.sandbox == 
> sandboxElement2.sandbox). Sandboxes without a "domain" attribute are 
> always isolated.
> 
> 3. Sandboxes can be nested, with each inner one being additionally 
> restricted by the outer.
> 
> 4. The script can find out that it's running in a sandbox. There's 
> nothing bad about it.
> 
> 5. There should be a discussion about what a sandboxed script can do. 
> Can it set window.location? Can it do window.open()? Maybe these 
> permissions should be governed by additional attributes to <sandbox>.
> 
> 6. A sandbox can specify a single JS error handler for all enclosed 
> scripts (to address known cases of scripts which are not ready for the 
> unusual environment they are in).
> 
> 7. Backward compatibility. The current browsers will ignore the unknown 
> <sandbox> element and give the enclosed scripts full access to 
> everything. This is not acceptable. As there is no way to disable 
> scripting inside a certain element in HTML 4, the HTML cleaners usually 
> found on sites live LiveJournal.com are still required. Here's what they 
> should do.
> 
> 7.1. There are new elements: <safe-script>, <safe-object>, <safe-iframe> 
> (did I forget something?). They are equivalent to their "unsafe" 
> counterparts, except that the existing browsers simply ignore them. HTML 
> cleaners should replace <script> with <safe-script> and likewise.
> 
> 7.2. HTML event handler attributes are mangled likewise: safe-onclick, 
> for example. Note that this doesn't affect the names of DOM properties 
> like element.onclick.
> 
> 7.3. A new URI scheme is introduced: "safe-javascript:". Likewise.

This is unfortunately far too complicated. It basically duplicates most of 
the <iframe> security and DOM model, which itself has been a big source of 
bugs over the years.


On Wed, 28 Dec 2005, Alexey Feldgendler wrote:
> 
> JS already has origin-checking in the sense that every function is bound 
> to its parent namespace (class, window, whatever). No extra 
> origin-checking is required beyond that. Functions inside the sandbox 
> are just bound to their isolated namespace, just like normal functions 
> are bound to the window namespace.

Actually the origin-checking in browsers is simpler than that. It only 
happens at certain very specific places, namely the Window interface entry 
points. If we want to add a security model here, it has to be at the 
Window level, which basically means a new browsing context.


On Tue, 14 Mar 2006, Alexey Feldgendler wrote:
> 
> No, it's not really a change in getElementBy* functions. Because there 
> have been no sandboxes before HTML 5, noone can really expect that these 
> functions treat sandbox elements the same as all other elements. Well, 
> sandboxes are "security barriers" by their nature, so it seems, at least 
> to me, quite natural to have getElementBy* functions stop at them.

We have had one kind of sandbox: browsing contexts -- and indeed, they 
break getElementById() at sandbox boundaries.


> > Therefore, I'd say this security issue should be left to be taken care 
> > of by web application authors themselves. It's impossible for specs to 
> > force authors to make secured apps.
> 
> It's not to force them, it's to help them. Sanitizing user-supplied HTML 
> is a very difficult task today, and new security holes in HTML cleaners 
> of many web applications are found again and again. I think that the 
> spec should make it easier to write a secure web application.

While I agree with the general sentiment, to design features to help with 
this we have to have very specific goals and use cases in mind.


> > The spec can't do much in these situations. Shall the spec provide a 
> > way for CSS files to *not* be applied in <sandbox>ed content?
> 
> CSS3 already has negation selectors that can be used for this:
> 
> *:not(sandbox) p { text-align: left; }
> 
> This makes all paragraphs left-aligned except in sandboxes.

No, it makes all paragraphs left-aligned so long as they have at least one 
ancestor that isn't a <sandbox> element. Since every element in HTML has 
at least one ancestor that isn't a <sandbox> element (they all have an 
<html> element parent), the above would match all elements (except the 
<html> element itself, ironically).


On Wed, 15 Mar 2006, Alexey Feldgendler wrote:
> 
> (A wild thought: maybe enforce ID uniqueness only for <!DOCTYPE html>?)

We don't want to do anything that is DOCTYPE-specific if we can help it.


On Thu, 16 Mar 2006, Alexey Feldgendler wrote:
> 
> I'm not speaking about enforcing ID uniqueness at the time of parsing 
> the page, but only at the time of calling getElementById(). I believe it 
> will break very few pages, if any.

According to my research, over 10% of pages have duplicate IDs. That's a 
lot of risk.


> And another thing: HTML 5 is about to make HTML pages more powerful, 
> there are going to be menus, datagrids and such, but most of these 
> features are useless without scripting, aren't they? For example, a 
> datagrid isn't really sortable at client side without a script, which 
> makes it useless in blogs and CMS unless they allow some scripting.

<datagrid> has since been changed to allow client-side sorting, FWIW. But 
your point is well taken.


> Of course, there is a lot more to think and talk about. I suppose there 
> are going to be problems with particular buggy implementations of 
> sandboxing and exploits specifically targetted at holes in such 
> implementations. I suspect that web application authors and site 
> administrators will be hesitant to allow user scripting even in 
> sandboxes because of the possible browser bugs.

Because of this, we really want to make sure we leverage as much of the 
existing infrastructure as possible. I'm worried that the DOMSandbox idea, 
with its "fake" documents, etc, introduces too much complexity.


On Sat, 27 May 2006, Alexey Feldgendler wrote:
> 
> I propose to define the notion of "side effect free script". All 
> browsers which allow scripts in declarations like CSS should only allow 
> side effect free scripts in such places.
> 
> A script thread should be started in side effect free mode if the script 
> is invoked from:
> 
> 1. Anywhere within CSS, including inline style attributes.
> 
> 2. Any javascript: URI of external stylesheets, scripts, objects and 
> such.
> 
> 3. Other ideas?
> 
> When a script thread is in side effect free mode:
> 
> 1. It stays in this mode until the thread completes.
> 
> 2. It can call any non-native function, but the same restrictions apply.

So it can get hold of data that the rest of the page has created, or is 
storing in its temporary variables (e.g. it can get hold of your calendar 
data if you're looking at an online calendar application).

> 3. It cannot assign any variables except locals.
> 
> 4. It cannot call any native function except those specifically marked 
> by the spec as side effects free. For example, sin() is side effects 
> free, and window.open() is not.
> 
> 5. It can read any property that can be normally read.
> 
> 6. It cannot assign any property for which a native setter function is 
> used.
> 
> 7. It cannot create new object instances except those specifically 
> marked by the spec as side effects free. For example, RegExp is side 
> effects free, and Image is not.
> 
> 8. Any attempt to break these restrictions should generate an exception.
> 
> 9. Optionally, execution time limit may be imposed on the thread, so 
> that it doesn't make the document unrenderable by running an endless 
> loop inside CSS expression().

With the above you could still do something like:

   <a style="display: expression(...)" href="http://evil.example.com?a">a</a>
   <a style="display: expression(...)" href="http://evil.example.com?b">b</a>

...where the first "..." script returns 'none' to convey one piece of 
information and 'block' to convey another, and the second is the reverse; 
the user who clicks on the link then exposes the bit of information the 
script was trying to steal. I'm sure there are more powerful attacks as 
well, e.g. using href=javascript: to return an HTML page with script.

In short, the complexity is high, as is the risk that it isn't 
comprehensive. Also, it seems to me that most scripts want to do something 
more fancy. For example, a calendar widget will want to talk to its 
server, render new DOMs, interact with the user, etc. What's the use case 
for these scripts? Are they common enough to warrant their own security 
model?



On Mon, 30 Oct 2006, Douglas Crockford wrote:
>
> I had been thinking that the solution was to replace JavaScript with a 
> capability language like E (http://erights.org/) and to replace the DOM 
> with a capability DOM. I am now thinking that a far less drastic 
> solution is required: a module facility that forms a trust boundary in 
> the page with a communications mechanism that does not allow capability 
> leakage.
> 
> It requires no changes to JavaScript and a small, incremental change to 
> HTML. The proposal is here: http://json.org/module.html

This is basically a subset of the functionality described here:

   http://www.whatwg.org/specs/web-apps/current-work/#crossDocumentMessages


On Tue, 31 Oct 2006, Douglas Crockford wrote:
>
> I am proposing the module tag instead.

It basically is the same as the <iframe> element in conjunction with the 
cross-document messaging functionality in the WHATWG spec.


> JSON is a safe subset of JavaScript. It is meaningful in a JavaScsript 
> context (which is where we currently are). If we ever have a 
> multilingual future (and I hope that we do), JSON has demonstrated 
> amazing interoperability.

Indeed. JSON can be used with the API cited above.


> I am a big fan of asynchronicity, but I don't think it is indicated in 
> this case. I want tighter temporal binding between the sender and the 
> receiver so that they can cooperate in event handling. Such patterns are 
> complicated if event handling is also the medium of communication. I 
> also want an exception raised on sending if there isn't a corresponding 
> receiver. This is an important indication of willingness and ability to 
> cooperate.

The postMessage() method in the API cited above is actually synchronous, 
much as in your proposal.


> Also I am worried about the event object itself. It is possible to 
> implement an event mechanism such that the event object is not itself a 
> conduit for capability leakage, but it is not required by the current 
> draft.

Could you elaborate on this?


On Fri, 15 Dec 2006, Dean Edwards wrote:
>
> When creating DHTML widgets (e.g. a colour picker) developers have to 
> battle the CSS cascade. A widget might be made of various HTML elements 
> which inherit style from the page. Sometimes inherited styles can mess 
> with the layout of your widget. So we end up coding stuff like this:
> 
> #widget div {margin: 0 !important; padding: 0 !important;}
> #widget input {border: 0 !important;}
> 
> You can't realistically cover all the cases so potentially a new style rule
> can ruin your widget layout completely.
> 
> So I'm suggesting a new element: <reset>
> 
> This element is in the document flow as normal except that it acts as a 
> blank canvas as far as CSS is concerned. Ideally, it should have no 
> style at all. Like this:
> 
> http://developer.yahoo.com/yui/reset/
> 
> The <reset> element acts as a container for HTML elements that are not 
> included in the CSS cascade. That way we can build and style widgets 
> without worrying about the page that will contain them.

This sounds very much like something you'd do with XBL, no? Could you 
elaborate on the use case? I mean, colour pickers are the poster child of 
XBL use cases.


On Fri, 15 Dec 2006, Dean Edwards wrote:
> 
> I would still like a way to prevent CSS inheritance though. So far the 
> suggestions are:
> 
> 1. <reset> element
> 2. inheritstyle="false" attribute
> 3. cascade:off; CSS property
> 
> I don't really mind which one it is. None of them seems particularly 
> wrong.

Is it really inheritance you want to stop? Wouldn't you want to stop all 
styles from applying at that point? e.g. if someone had:

   * { border: solid; }

The 'border' property isn't inherited, but would yous till want the border 
rule to not apply to the "widget"?


On Sun, 17 Dec 2006, Paul Arzul wrote:
> 
> (another) common problematic example is google cache. style can 
> overreach and create a mishmash of unreadable overlapping text and 
> images. for an example, see the top of the page on wikipedia's entry for 
> whatwg:
> 
> <http://209.85.129.104/custom?q=cache:0s8ftW8HviQJ:en.wikipedia.org/wiki/Web_Hypertext_Application_Technology_Working_Group>
> 
> something that can help is if each party using style limits it's reach 
> by css signature (eg. <body id="www-whatwg-org"> and then add the id 
> selector prefix #www-whatwg-org to each style rule). mixed html shares 
> the same canvas, so this won't help when absolute positioning interferes 
> with layout-- as is the case with the google cache example.
> 
> do browsers make it easy enough to disable style that we can ignore 
> this? anyone mixing html seems to run into this problem-- i've seen it 
> in bookmarklets and greasemonkey scripts too.
> 
> advertisers seem to use <iframe>-- although minor tweaking is required: 
> opera inherits the base document's background-color, mozilla doesn't. i 
> couldn't find the correct behaviour defined-- any pointers?

This seems like a CSS issue to me. If you change the markup without 
looking at the styles (the way the Google Cache does) then you should 
expect weird things to happen.


> if this is not out of scope, perhaps we can help vendor clarity by 
> mentioning which elements (eg. <iframe> and <object>) should typically 
> "reset" style?

The styles "reset" when you have a new Document. It's quite simple. :-)
CSS covers this (possibly implicitly).


On Fri, 12 Jan 2007, James M Snell wrote:
> 
> I've recently been musing over some ideas around sandboxing scripts and 
> styles within a document [1].  The basic idea is to have some means of 
> isolating potentially untrustworthy scripts.
> 
>   From my blog entry: "Scripts within the sandbox would only see the DOM
>   of the sandbox. Methods defined outside the sandbox would still be
>   accessible. External methods could return objects from outside the
>   sandbox."
> 
> The example I go on to give is this:
> 
>   <html>
>   <body>
>     <script>
>       function getElement(id) {
>         return document.getElementById(id);
>       }
>     </script>
> 
>     <sandbox id="a">
>       <div id="a1"></div>
>       <script>
>         // this will fail because b1 does not exist in sandbox a
>         document.getElementById('b1').innerHTML = "foo";
> 
>         // this will succeed because getElement(id) can be called from
>   within sandbox a
>         getElement('b1').innerHTML = "foo";
>       </script>
>     </sandbox>
> 
>     <sandbox id="b">
>       <div id="b1"></div>
>     </sandbox>
>   </body>
>   </html>
> 
> The use of the sandbox tag is purely illustrative.  As Asbj??rn Ulsberg 
> points out in the comments on my entry, the same effect could be 
> achieved using either a new DOM and/or CSS property. For instance, we 
> could replace the <sandbox> with <div style="scripts:restricted"> (or 
> some variation thereof).
> 
> Whatever shape the mechanism ultimately takes, having a way of isolating 
> scripts within a document would be extremely beneficial.

This is similar to some of the other ideas. It's not entirely clear what 
this solves exactly, though, nor how it should work (e.g. does it require 
per-method-invocation security checks? That's expensive).


On Fri, 12 Jan 2007, Asbj?rn Ulsberg wrote:
> 
> Frames are a terrible solution. The content is after all a part of the 
> page it's hosted in, but we want to sandbox it to make sure it can't do 
> any harm.
> 
> Let's say we'd like to sandbox anonymous user-contributed comments on a 
> blog, but not comments from logged in users. That would require all 
> anonymous comments to be placed within an iframe. For 100 anonymous 
> comments, that's 100 iframes on a single web page. Don't tell me that's 
> an elegant solution.

Why not? Or rather, why is a 100 <sandbox> frames (or whatever) better? 


On Fri, 12 Jan 2007, James M Snell wrote:
> 
> Again, I'm not proposing any particular solution.  Nor am I saying there 
> aren't already existing solutions to these problems that can work.  
> What I'm saying is that having some way of isolating a script would be 
> ideal and I was curious as to what others had to say about it.

We have a way to isolate a remote script -- run it in an <iframe>. I'm not 
sure I really understand the use case or requirement here.


On Sat, 13 Jan 2007, Mike Schinkel wrote:
> 
> A community site could allow user-contributed script to add 
> functionality to the community on sites such as free-form as a wiki, and 
> hence with open-ended use cases. But that's not really possible today 
> because the almost certainty of maliciousness.

I'm not sure how it could be possible, really.


> I'd rather not describe it explicitly yet, but consider a situation 
> where I have a script that operates on a section of HTML that allows 
> plugs-in from arbitrary URLs.  A webmaster could use this but would have 
> to trust that the webmaster of the plugins would not change their script 
> after he used them and thus would be much less likely to use this 
> functionality. If he could sandbox it, that requirement for trust would 
> be diminished and it would increase the likelihood the use of the 
> functionality would spread. FYI, an IFRAME would NOT work for this 
> use-case as it is about linking script files ot the main document, not 
> about visual widgets.

Without much more detail, it's hard to really analyse this case.



PROBLEMS

There are several areas where we could help matters.

* Insertion of non-markup data into:
   - the body of the page
   - text attributes
   - URI attributes
   - parts of URIs in attributes
   - strings in scripts
   - CSS in external files
   - CSS in <style>
   - CSS in style=""
   - etc

* Insertion of limited markup into the page body, e.g. blog comments or 
forum posts, allowing a subset of site-hosted images, allowing links (but 
only with certain values, forcing them to have rel=nofollow), allowing 
certain semantic markup idioms (<del>, <cite>, <i>, <ul>, <dialog>) but 
disallowing anything script- or style- related.

* Insertion of limited script-enabled content, e.g. animated scripted SVG 
or HTML, without allowing the script to access content on the site to 
perform any kind of cross-site scripting, or e.g. to enable widgets to be 
included safely. This can be further subdivided into two different cases:
   - Only block cross-site scripting (basically, allow the page to do 
     anything, but pretend it is from another domain)
   - Block anything that could be obnoxious (e.g. alert(),
     window.open(), etc)

There are also other use cases, but I'm not clear on what those are. We 
didn't really see much detailed descriptions of problems in the threads on 
the topic.


SOLUTIONS

Inserting a block of non-markup content safely could be done in HTML (not 
XML) by introducing a new feature to the parser, which switched the parser 
into "plaintext" mode for a certain number of characters:

   <body>
    <p>Hello, you said:
     <?insert size=11?>Hello World
    </p>
   </body>

Then, if the user tries to insert something bad like 
"<script>alert(window.cookie)</script>" it would just be treated as plain 
text and nothing bad would happen:

   <body>
    <p>Hello, you said:
     <?insert size=37?><script>alert(window.cookie)</script>
    </p>
   </body>

...nothing bad would happen in new UAs.

However, servers would still have to do processing server-side to handle 
legacy UAs. Furthermore, servers would have to count characters the same 
way as the user agent. For example, that would mean that we would have to 
define exactly how many characters a malformed UTF-8 sequence is, and even 
then, we'd have to rely on the server being able to handle it. This is 
actually much more complicated than just escaping all < and & characters, 
and thus would actually probably make things worse.

We can't do something like this:

   <body>
    <p>Hello, you said:
     <sandbox>Hello World</sandbox>
    </p>
   </body>

...because nothing stops the user from inserting "</sandbox>" into the 
string -- e.g. if the user tried to insert 
"</sandbox><script>alert(window.cookie)</script>" the result would be:

   <body>
    <p>Hello, you said:
     <sandbox></sandbox><script>alert(window.cookie)</script></sandbox>
    </p>
   </body>

...and the result would be the script executing. Now we could exand that 
by putting, e.g., a hash into the <sandbox> element's attributes:

   <body>
    <p>Hello, you said:
     <sandbox md5="e59ff97941044f85df5297e1c302d260">Hello World</sandbox>
    </p>
   </body>

...but that doesn't actually help us determine where the end should be. 
For example consider the case where the user tries to insert the 
following:

   Hello World</sandbox>
    </p>
   </body>
   <script>alert(window.cookie)</script>
   <!--

When you insert the data, it ends up like:

   <body>
    <p>Hello, you said:
     <sandbox md5="06fa1b24b7c533055bddabbbae04b77b">Hello World</sandbox>
    </p>
   </body>
   <script>alert(window.cookie)</script>
   <!--
   </sandbox>
    </p>
   </body>

...which has a non-matching md5sum for the first part, but that doesn't 
really help us -- where should we stop parsing? Aborting the rendering 
altogether would just change this from an XSS attack to a DOS attack, 
which isn't much more pleasant.

You could say that the user must simply remove all instances of 
"</sandbox", but that at that point I wouldn't be able to post this e-mail 
in a blog comment (as it contains the string "</sandbox"). Requiring, 
then, that the server escape the < characters just leads us back to where 
we are today, with this strawman <sandbox> not having helped us at all.

And here we haven't even discussed the possibilities of the attacker 
faking the hash so that the hash for the part up to the attacker's 
"</sandbox>" end tag is the same as the sum for the whole thing (which 
would prevent the element from working at all). MD5 and SHA-1 are both 
broken to some extent or another. In fact, in this case even a brute force 
attack (e.g. leveraging the millions of machines that the attacker might 
already have compromised) would be a problem, as it would only take a 
single known hash collision with some suitable plain text (where 
"suitable" means that it contains the payload after the </noscript>) to 
permanently break <sandbox> for an entire generation of browsers.

In fact I cannot see _any_ solution to the problem of allowing safe and 
server-side-free inclusion of arbitrary text in the body of an HTML page 
that doesn't have obvious attacks or limitations.

The current solution for server-side inclusion of arbitrary text is well 
known: escape all & and < characters in the string to their HTML entities.


The solutions listed above only address the body of an HTML document; one 
might want to introduce markup in many other parts of a page. However, the 
syntax of the language constrains us even further in finding ways to put 
data there. For instance, to do the same kind of thing for attributes 
would require some new attribute syntax:

   <a href="..." ?21 title="This is inserted text"> ...

...(where the ?21 means "the next attribute has 21 characters), but that 
has all the problems described above, and is thus no simpler to do in 
practice than simply escaping all " and & characters.

I can't really see _any_ way of doing safe insertion (that is, as safe as 
you can get by the methods above) of strings into attributes containing 
URIs, or into the middle of those URIs, given the desire to avoid systems 
that redirect to javascript: URIs and the like.

Similarly, I couldn't see a good way of labelling where strings are going 
to be inserted into scripts (either externally, in <script>, or in onfoo 
attributes) or stylesheets (externally, <style>, style="") to ensure their 
proper escaping.


Here are some general rules for safety when handling user input that isn't 
CSS, JS, or HTML, when you want to embed it into CSS, JS, or HTML.

Always know the encoding of input you receive, and convert it to UTF-8 
immediately. Store it as UTF-8.

Always output UTF-8, with Content-Type headers that specify UTF-8.

If you want to insert text into a URI component (e.g. a query), the 
simplest way is to convert the string to UTF-8, then encode all bytes that 
aren't in the ASCII a-z A-Z 0-9 range as %XX where XX is the hexadecimal 
representation of the byte's value.

Always quote JS and CSS strings that are to take user input with single 
quote characters.

If you want to insert text into a JS or CSS string delimited by single 
quote characters, escape all \, / and ' characters by prefixing them all 
by a single \ character.

Always quote attribute values that are to take user input with double 
quote characters.

If you want to insert JS, CSS, or a URI into an attribute delimited by 
double-quote characters, you need to escape all & and " characters by 
replacing them with their entity equivalents (&amp; and &quot; 
respectively).

If you want to insert text into the body of an HTML file, or into a 
<title> element, escape all & and < characters by replacing them with 
their entity equivalents (&amp; and &lt; respectively).



Insertion of limited markup is even harder for two reasons. First, nobody 
agrees on exactly what markup to limit it to. Secondly, you have all the 
problems described in the previous set of features, except that the 
parsing is even more complex.

One way to do it would be to encode the markup into a base64-encoded data: 
URI and then point an <iframe> at it:

   <iframe src="data:text/html;base64,PHA%2BVGhpcyBpcyBteSBzYW1wbGUgbWFya3VwITwvcD4%3D"
   ></iframe>

...of course then we'd lose the styling, so we'd have to have some sort of 
way to make the styling go through:

   <iframe src="data:text/html;base64,PHA%2BVGhpcyBpcyBteSBzYW1wbGUgbWFya3VwITwvcD4%3D"
           let-style-through
   ></iframe>

...and we'd need a way to make sure the UA blocked the dangerous stuff:

   <iframe src="data:text/html;base64,PHA%2BVGhpcyBpcyBteSBzYW1wbGUgbWFya3VwITwvcD4%3D"
           let-style-through
           disable-scripting-and-other-dangerous-things
   ></iframe>

"Dangerous things" might include forms and styling (to prevent phishing).


At this point though we're very close to the next and last thing on my 
list, namely insertion of carefully controlled content with scripting.

The sanest way I can see of limiting scripting is to give it its own 
browsing context (aka scripting context, or global scope). Anything short 
of this would make the security model overly complicated -- the security 
model is what we want to keep at its simplest, as I've said several times 
in this e-mail.

This basically implies an <iframe>, again possibly with the data in a 
data: URI, and combined with a way to ioslate the content in the <iframe> 
from the content of the parent browsing context:

   <iframe src="data:text/html;base64,PHA%2BVGhpcyBpcyBteSBzYW1wbGUgbWFya3VwITwvcD4%3D"
           isolate-scripts
   ></iframe>

In addition, if we want to enable untrusted scripting without enabling
annoyances, we could add an attribute that sets a flag on the browsing 
context to disable certain APIs including window.alert and the ability to 
open new browsing contexts:

   <iframe src="data:text/html;base64,PHA%2BVGhpcyBpcyBteSBzYW1wbGUgbWFya3VwITwvcD4%3D"
           isolate-scripts
           disable-annoyances
   ></iframe>

The names above are a bit long; here's a summary of what the four modes 
could be:

   seamless - if present, styles cascade through the browsing context 
   boundary; ignored if the origin doesn't match the parent's.

   noscript - disables all scripts in the embedded page

   isolate - make the origin of the file not match the parent's, 
   regardless of the real origins

   restrict - disable certain APIs in the browsing context

What do people think?


In addition to the above, another area that currently involves scripts 
which could be sandboxed is one Hallvord pointed out, namely getting JSON 
data from a remote server. In my opinion this is better served by using a 
safe data download mechanism such as XXX (the Cross-site Extensions to 
XMLHttpRequest that I proposed to the W3C eleven months ago [1]), coupled 
with a safe parser mechanism (e.g. using XML, or a JSON parser).

[1] http://lists.w3.org/Archives/Public/public-webapi/2006Jun/0012

-- 
Ian Hickson               U+1047E                )\._.,--....,'``.    fL
http://ln.hixie.ch/       U+263A                /,   _.. \   _\  ;`._ ,.
Things that are impossible just take longer.   `._.-(,_..'--(,_..'`-.;.'

Received on Monday, 7 May 2007 20:50:38 UTC