Tuesday, March 13, 2007

(almost) Rending HTML without JavaScript

I was researching a way to allow users to upload HTML but not allow the browser to execute javascript. A solution like this would go along way towards mitigating persistent XSS issues. A couple of days ago, I thought I had stumbled across a odd behavior in Firefox that would allow me to do exactly that. See the code below. Turns out one of WhiteHat engineers pointed out a flaw fairly quickly, but I figured I'd post this because what the browser does it weird. ARGH, so close.

When writing HTML content to an iframe using the "magic" line, tags do not execute, while event handlers do. What also happens is the DOM security, or domain, gets all messed up for some reason and you get errors on valid function calls.

<* html>

<
* script>

function addInput() {


var input = document.getElementById('input');

var output = document.getElementById('output');


if (! output.contentWindow.document.body) {
output.parentNode.removeChild(output);

addOutput();

output = document.getElementById('output');

}


/* MAGIC! */
output.contentWindow.document.body.parentNode.innerHTML = input.value;

}


function addOutput() {

var container = document.getElementById('container');

var output = document.createElement('iframe');
output.id = "output";
output.style.width = "600px";
output.style.height = "200px";
container.appendChild(output);
}


<
* /script>

<
* body onload="addOutput();">

Input:<
* br />
<* textarea id="input" style="width: 600px; height:200px;"><* /textarea><
* br>

Output:<
* br />
<* div id="container"><* /div><
* br><* br>

<
* input type=button onclick="addInput()" value="Input">

<
* /body>
<
* /html>


1 comment:

Sid Stamm said...

I think this behavior (not executing scripts in script tags during the magic) is to be expected, though it does initially seem counterintuitive. According to the w3schools website:

Code within this element is executed immediately when the page is loaded, if it is not in a function.

That suggests that script tag contents are evaluated only as the page is loading, and once it is finished, they are ignored, or rather simply treated like the HTML these tags are.

You can chain the creation of script tags (i.e., script in one tag causes more script tags to be added to the DOM) because the scripts execute while the document is loading. (alluded to by the W3C HTML4 docs)

Anything that is inserted into the global environment (or its descendant scopes) during loading persists, thus the ability to use event handlers.

What is interesting is that the newly created iframe's document has the domain of about:blank, no matter where the containing page is served from... even after changing the body's content. A brief search brings up Wouter Demuynck's blog entry about similar behaviors. I wonder if there's anything else (given the "weirdness" experienced) that can be done...