The WHATWG Blog

Please leave your sense of logic at the door, thanks!

What’s next in HTML, episode 2: who’s been peeing in my sandbox?

by Mark Pilgrim, Google in What's Next

Welcome back to “What’s Next in HTML,” where I’ll try to summarize the major activity in the ongoing standards process in the WHAT Working Group. With HTML5 in Last Call, the WHATWG has moved to an unversioned development model for HTML. While browser vendors are busy implementing HTML5, let’s talk about what’s next.

The big news in HTML this week is r1643. ... Well, technically that revision is over 20 months old, but there have been a flurry of updates that affect the underlying feature. What feature, you might ask? Sandboxing untrusted content.

The sandbox attribute, when specified [on an <iframe> element], enables a set of extra restrictions on any content hosted by the iframe. ... When the attribute is set, the content [hosted by the iframe] is treated as being from a unique origin, forms and scripts are disabled, links are prevented from targeting other browsing contexts, and plugins are disabled.

This could be useful for all kinds of scenarios. The HTML5 spec lists some examples of blog comments, but I think that’s mostly a red herring. Think about what’s hosted in iframes today: third-party advertising and third-party widgets. In each case, a web author wants to embed something on their page that they have little or no control over. In practice, that usually works fine. Advertising iframes don’t do anything (except display ads). Most widgets are well-behaved, and most widget frameworks (like Google Gadgets) enforce terms of service that forbid widgets from “taking over” the parent page in which they are embedded. Still, that’s a social/legal solution, not a technical one. Sandboxing is a complementary technical solution, where the parent page can actually tell the browser “Hey, I don’t fully trust this thing, but I’m embedding it anyway. Can you reduce its privileges?”

What privileges? Well, by default, “sandboxed” iframes can not

There are ways for the parent page to add back each of these privileges, if the third-party content needs it.

[The sandbox attribute’s] value must be an unordered set of unique space-separated tokens. The allowed values are allow-same-origin, allow-forms, and allow-scripts. The allow-same-origin keyword allows the content to be treated as being from the same origin instead of forcing it into a unique origin, and the allow-forms and allow-scripts keywords re-enable forms and scripts respectively (though scripts are still prevented from creating popups).

So it’s a security feature. You could restrict an advertising iframe to have no privileges whatsoever, but you could give a widget iframe privileges to execute its own scripts or embed its own forms.

If it’s a security feature, won’t older browsers still be insecure?

Yes. Well, no more than they are now. In fact, very few browsers support the sandbox attribute today, so we’re not just talking about users of older browsers — we’re talking about pretty much everyone. But that’s OK. The sandbox attribute is designed to be an incremental security feature. It’s an additional layer of security, not the only layer. Browsers have supported iframes for a long time, and thousands of web authors are using them despite the very real risks of embedding untrusted content. Advertising networks can and have been hacked; malicious widgets can and have been published; bad actors can and do try to do bad things to as many people as possible until they’re caught and taken down. You need to keep doing all the things you’re doing now to prevent iframe-based attacks. Then add sandbox, too.

I can’t do any filtering or sanitizing. Can I rely solely on browser-based sandboxing?

Someday, you might — might! — be able to throw out all your sanitizing code and rely solely on the sandbox attribute. Of course, you can’t do that today, because users of older browsers would still be vulnerable. So we need a “clean break” solution — a way to serve untrusted content to supporting browsers while absolutely, positively, 100% ensuring that older browsers never render the untrusted content under any circumstances. Enter the text/html-sandboxed MIME type.

All HTML pages are served with the text/html MIME type. It’s part of the HTTP headers, normally invisible to end users, but nevertheless sent by web servers every time a client requests a page. Every resource type (images, scripts, CSS files) has its own MIME type. Untrusted content could have its own MIME type. And this is where text/html-sandboxed comes in. If my web server serves up an HTML page with a MIME type of text/html, your browser will render it. If my web server serves up the same HTML page with a MIME type of text/html-sandboxed, you browser will download it (or offer to download it). Your browser doesn’t recognize that MIME type, so it falls back to the default action, which is to download it and save it as a file on your local disk. We can use this behavior to our advantage.

As browsers start supporting the sandbox attribute, they can also start supporting the text/html-sandboxed MIME type. What does it mean to “support” this new MIME type? If a user navigates directly to a page served with the new MIME type, don’t do anything special. Just download it, which is what happens already. BUT... if the user navigates to a page that includes an <iframe> element, AND the iframe has a sandbox attribute, AND the src of the iframe points to an HTML page that is served with the text/html-sandboxed MIME type, THEN render the iframe as normal (but still subject to the restrictions listed in the sandbox attribute).

Older browsers will download (or offer to download) the untrusted content. From a security perspective, that’s a good thing — at least, it means the content won’t be rendered as HTML. From a usability perspective, that’s terrible. Who wants to go to a page and suddenly have the browser offering to download a bunch of useless files? That means that you won’t really be able to use this technique until all users have upgraded to a browser that supports both the sandbox attribute and the text/html-sandboxed MIME type. That will be... a while. But it might happen someday!

Iframes suck. Can’t I just include the untrusted content inline?

There have been a number of proposals for a <sandbox> element, which you could wrap around untrusted content. All such proposals suffer fatal flaws, stemming from how today’s browsers parse HTML markup. You, the author who wants to “wrap” untrusted content, would need to ensure that the content did not “break out” of the sandbox. For instance, it could include an </sandbox> element. (Hey, it’s untrusted! That’s why we’re here in the first place.) There are a surprising number of variations of markup that are recognized as end tags (having to do with inserting whitespace characters in strange places), and you would be responsible for sanitizing all of these variations. Furthermore, you would need to ensure that the untrusted content did not include a script that called document.write(), which could be used for writing out a matching </sandbox> end tag programmatically. Think about the number of ways that script could be obfuscated, and pretty soon you’re asking individual web authors to solve the halting problem just to wrap some untrusted content.

If a wrapper element is the wrong solution, what’s the right one? This is where the “flurry of updates” has been happening. The current solution is r4619: the srcdoc attribute (with minor updates in r4623, r4624, and r4626). The best way to explain it is by example:

<iframe sandbox srcdoc="<p>Markup in an attribute, woohoo!</p>"></iframe>

Yeah, that’s pretty janky. But it has the following nice qualities:

It also has the following not-so-nice qualities:

There is one exception to that last rule. There are a few comment systems that are entirely client-side. That is, the comments are not part of the page markup that comes down from the web server; they are programmatically added after the page is rendered. Such comment systems could use JavaScript-based feature detection to check whether the browser supported the srcdoc attribute, and write out the appropriate markup either way. I wrote the book on HTML5 feature detection. (No really! A whole fscking book!) Detecting srcdoc support would use detection technique #2:

if ("srcdoc" in document.createElement("iframe")) { ... }

But this would only help in the case where you were adding untrusted content to the page at runtime, on the client side. Server-side cases will have to wait until everybody upgrades.

So when can I use all this stuff?

Hahahahahaha. You must be new here.

No really, when?

There are several pieces here, each with their own compatibility story.

  1. The sandbox attribute, for reducing privileges of untrusted content. Chromium and Google Chrome support the sandbox attribute (I tested the dev channel version 4.0.302.3); Safari, Firefox, Internet Explorer, and Opera ignore it. So you can start using the sandbox attribute today — just be sure to test in Chromium or Google Chrome to ensure you’ve set the sandbox privileges properly. It won’t have any effect in other browsers, but that’s OK. Remember, the sandbox attribute isn’t designed to be your only line of defense; it’s a complement to your existing defenses. Keep doing whatever you’re doing now (sanitizing input, auditing code, enforcing legal terms with your partners, etc), then add sandbox for extra protection.
  2. The text/html-sandboxed MIME type, for ensuring that users can’t navigate to untrusted content. There are two parts to this. First, browsers must not render pages served with a text/html-sandboxed MIME type, if you navigate to the page directly. This part works in all browsers, today; they all download (or offer to download) the page markup instead of rendering it. Second, browsers that support the sandbox attribute need to render iframes served with the text/html-sandboxed MIME type (subject to the privilege restrictions listed in the sandbox attribute). No browser supports this yet, not even Google Chrome. (It renders the parent page but downloads the iframe content instead of rendering it within the frame.) So you can’t use this technique yet, until Google updates Chrome to support it. (In theory, other browser vendors will implement support for this at the same time they implement support for the sandbox attribute, but I suppose we’ll just have to wait and see.)
  3. The srcdoc attribute, for including untrusted content inline. Since the fallback behavior in legacy browsers for this feature is “render nothing at all” (by design), this attribute won’t be useful until pretty much all of your visitors upgrade to browsers that support the attribute. At the moment, no current browser supports the srcdoc attribute, so it’ll be a while. If I had to guess, I’d say January 29, 2022, at 4:37pm. Plus or minus 10 years.

And now you know “What’s Next in HTML.”

24 Responses to “What’s next in HTML, episode 2: who’s been peeing in my sandbox?”

  1. 1. Why `text/html-sandboxed`? and not some form of `text/untrusted+html`? Isn’t this the convention?

    2. Why a separate srcdoc? Couldn’t this be done with data-urls?

    3. Can’t content be placed inside comments or CDATA sections (as a child of the iframe element)? I’m not an expert, but this kind of content shouldn’t be parsed either.

    BTW, I believe that you were talking about the „srcdoc” attribute, but typed in „sandbox” (in #inlining-is-hard-lets-go-shopping section, first list).

  2. I don’t really get how text/html-sandboxed is supposed to work. For the content to be sent as this MIME-type you’d have to be the one controlling the server sending the content. Why would you use an iframe to include content from your own site?

  3. data: is supported by current browsers, so it’s out of the question (it’d be insecure).

    CDATA is variant of <sandbox> and it’s useless for the same reason.

  4. Well, I’m not sure CDATA can be compared to {sandbox} or any other tag, since it’s contents shouldn’t be parsed. So all you have to be sure of is not to include closing “]]}”, without the whole script document-write-mumbo-jumbo risks.

    As for the `srcdoc` — ah, I think I get the point. It’s a kind of “render nothing, or render this content in a secure manner” attribute? That would explain it, but it’s not elegant at all. Not only the fact that this is markup in an attribute, but also this is a yet another way to provide the content. And if not for the existing browsers, nobody would ever think of it.

  5. What about *trusted* IFrames? I have the joy of working on systems that, for various business reasons, need to span multiple domain names. I’d _love_ to be able to see a boolean “trusted” property – <iframe trusted=”false”> instead of <iframe sandbox>, and also <iframe trusted=”true”>.

    As for srcdoc: first, why not simply use a data URL? Second, I don’t understand the parsing argument. Today, an IFrame content is parsed as a complete HTML document, and there is simply no way a tag can “break out” of it. I don’t see why an couldn’t be simply be parsed *as if* it is sandwitched inside an <html><body>…</body></html>EOF envelope, and be done.

  6. Why is this functionality listed in the W3c Editorial Draft? This blog post suggests that it’s an “addition to HTML5 afterwards”, like the device element, but the device element is not in de W3c Draft.

  7. Why srcdoc? There’s already a data uri scheme that could be used:

    It does need additional encoding. This may be a bonus as implementors will not get away with not encoding. anything (“Escaping quotation marks? Bah, no one needs quotation marks in there.”)

  8. I am not fully up to date on the latest HTML/CSS specs, so I will apologize if what I am proposing here has already been covered elsewhere.

    On the subject of iFrames, many of us have come across the age old problem of trying to automatically set the height of an iFrame based on it’s content. All the possible solutions that I have come across on the web, using JavaScript or jQuery code, do not work with content from external domains and also do not work consistently across different browsers. I would therefore like to propose that, as part of HTML standards, the addition of the following attributes to the <iFrame&gtl tag:

    AUTOHEIGHT, AUTOWIDTH, maxHeight and maxWidth.

    Example: <iFrame id=’…’ src=’…’ height=’300′ maxHeight=’2000′ AUTOHEIGHT >

    In this case the minimum height of the iFrame will be set to 300, however the browser will automatically adjust the iFrame height if the height of the content is greater than 300. This should work even if the content is from external domains. If maxHeight is specified than the maximum height of the iFrame will be limited to the value specified.

    I think that the addition of of these attributes will help thousands of developers like myself and eliminate the need for any JavaScript code to provide this highly useful functionality.

  9. “Think about the number of ways that script could be obfuscated, and pretty soon you’re asking individual web authors to solve the halting problem just to wrap some untrusted content.”

    But what about using server generated random boundaries? e.g.:
    [sandbox boundary=”fs98duf2l3jr8r”]
    …untrusted…
    [/sandbox boundary=”fs98duf2l3jr8r”]

    The idea being the UA wouldn’t parse anything until finding both boundaries, which the untrusted content can’t *possibly* know how to replicate.

    It’ll probably take 10 years before such a tag becomes useful (given how long it will take all browsers to get the capability)… so please let’s get it done ASAP. (Or tell me how it won’t work…)

  10. One other use of text/html-sandboxed could be to solve your UA sniffing issue – if the browser sends Accept: text/html-sandboxed then could you assume it can handle the inline srcdoc (or data uri) in the sandboxed iframe?

  11. I think the data uri scheme can be used instead of a srcdoc attribute while preventing older browsers of downloading untrusted code.

    The simple solution is to add the mimetype of text/html-sandboxed to the data uri.

    For example, using

    data:text/html-sandboxed,<html><h1>Hello World</h1></html>

    instead of

    data:text/html,<html><h1>Hello World</h1></html>

    If you try the first one on a browser that supports the data uri, it will ask to download the file (tested only with Firefox), thus solving all security problems related to browser support of data uris and sandboxed content.

  12. The iframe in this <a href="data:text/html,<iframe sandbox src=%22data:text/html,This frame is document.write(‘not’) sandboxed!%22>Yo!”>test document will tell you whether your browser is sandboxing the iframe (or, at least, whether scripts are allowed to run in the iframe). If you remove the sandbox attribute, it should say ‘This frame is not sandboxed!’. 😉

  13. text/html-sandboxed requires web devs to hold providers of 3rd party code accountable. Which we haven’t been good at so far. It seems that many execs in web companies suffer the dancing bunnies problem[1], and will do anything to get theirr ads/analytics/widgets.

    I’m not a fan of srcdoc, data uris, or any spec that requires devs to think like hackers or ninjas. We’re trying to protect ourselves from those types 🙂

    It’s too bad that wont work. What about super-strict restrictions on nesting and only allowing a subset of js within. I think a safer version of js(barring doc.write() etc) is long overdue, but I guess that might be beyond the scope of whatwg.

    [1]: http://www.codinghorror.com/blog/2005/07/the-dancing-bunnies-problem.html

  14. Also nice about IFrames is that any XML/XHTML errors inside it will only affect the IFrame itself, instead of making the entire page unrenderable.

  15. Actually, I am not sure whether that feature already exists, but if it doesn’t I suggest it be added because it will boost XHTML’s popularity by fixing one major issue.

  16. Interesting article. Can’t WAIT for this to be fully implemented!
    As far as <sandbox> goes… why not treat it as a separate top-level document? You could then place it anywhere you would a normal tag, the document level scripts could write to it (i.e: document.sandbox[0].etc) and any scripts in it could ONLY affect it. So, there would be no parent, opener, etc. tags.

  17. Ready to run test html here:
    http://is.gd/f2VxES

    Article ( http://is.gd/Kym8J1 ) confirming that months later, some major browsers are still dragging their heels on implementing the sandbox html5 security feature.

    Bit disheartening that whilst Chromium has implemented the feature, neither firefox 3.6 nor firefox4 seem to be making it work.

  18. Actually no one(*) wants that the sandbox attribute or elements like section, nav, article to work correctly or get broad acceptance. These WILL allow for Ad-Blocker software & plugins to do their work way better than nowadays. Face it; many Sites are financed solely by Ads. Removing them, or making this easier is not desired.

    If it is sandboxed content there is no way this is important, it is spam so let’s drop it. If it is not within the article element then it is spam, let’s drop it. That’s how ad blocker software will work in just some time…

    The only solution would be to integrate the ads directly into the content which will make ads even more annoying than they are.

    (*) That is ANY big company that earns money from web advertisement. Like -almost any- news site or news blog, -almost any- video sharing site, -almost any- search engine, and so on.