DOM-based XSS

From mediawiki.org

About DOM-based XSS[edit]

DOM-based XSS (or type-0 XSS) is a type of Cross-site scripting attack that occurs when client-side scripts (such as JavaScript) manipulate the page's DOM, allowing an attacker to run JavaScript in the victim's browser.

This class of XSS is distinct from Reflective XSS (type-1 XSS) and Stored XSS (type-2 XSS), since the server is not returning executable JavaScript to the browser. Instead, data that has been sanitized by the server, or possibly never sent to the server, is converted to executable JavaScript by the existing code running on the page.

For example, consider this example script from the OWASP website:

...
Select your language:
<select><script>
document.write("<OPTION value=1>"+document.location.href.substring(document.location.href.indexOf("default=")+8)+"</OPTION>");
document.write("<OPTION value=2>English</OPTION>");
</script></select>
...

If the page is loaded with the 'default' parameter set to '<script>alert("xss")</script>' instead of the intended language string, then the extra script will be added into the page's DOM and executed as the page is loaded.

Preventing[edit]

Although JavaScript can be added to MediaWiki sites without a formal code review process, these scripts can put other users in danger if they don't prevent these attacks.

As with standard XSS prevention, you should validate the data coming in when possible, and always escape the data as your script writes out to the page. In order to do this escaping, you should avoid using some methods that make escaping difficult, and instead use methods that make separation of HTML structure and values or text easy.

It can be tempting to use data attributes to provide information for javascript. This is generally good practice, but remember that users are allowed to make elements with arbitrary data attributes too, so any information taken from a data attribute (e.g. using jQuery's .data()) should be treated as untrusted input and validated.

Avoid[edit]

In general, these functions should be avoided if possible. They can be made secure, but require an understanding of how each browser's JavaScript engine works to make sure that all XSS vectors have been mitigated.

Element's .innerHTML() and .outerHTML() methods

element.innerHTML = '<div>' + user-data + '</div>';

JQuery's .html() method (which uses .innerHTML)

$('#target').html( user-data );

Using user data in jQuery's element creation

$( '<div id=' + user-data + '></div>' );

jQuery's append method, when you are not certain of the type and value of every node or object you are appending. If it is a string of (apparent) HTML, jQuery will implicitly convert it to an element.

document.write() or document.writeln()

document.write( 'Welcome to ' + user-data + '!' );

Using user data in a string passed to eval, setTimeout, an object's event handler, or javascript: url targets

Using user data in strings that generate CSS

Use[edit]

When updating the page's DOM, you should typically use this pattern of functions for creating DOM elements and inserting them into the DOM:

var $el = $('<div>');
$el.attr( {
  anAttribute: 'aValue',
  anotherAttribute: 'anotherValue'
} );
$el.text( 'Text of the div' );
$parent.append( $el );

As shown, you can use $('<tagName>') to create the element, and any attributes that do not include user-controlled data. You can also use .attr() to set the element's attributes.

When you have a correctly constructed element, you can use .append() or .appendTo to insert the element into the DOM. But see above; do not use append() or appendTo() if you do not know exactly what you are appending.

The .text() method can be used to set the text body of an element, and will correctly escape any HTML.

If you're not using jQuery, you can use

var element = document.createElement( "tagName" );
element.setAttribute( "attributeName", "value" );
element.appendChild( child );

Because the JavaScript is able to clearly interpret what strings are meant to represent element names, attribute names, and attribute values, it will correctly escape any HTML characters that may have been inserted into user-controlled data.

Sanitization[edit]

There may be times when you will need to mix user-controlled data with HTML. In those cases, you will need to ensure that the user-controlled data is properly escaped for the HTML context where it is being inserted.

For example, inserting data into the text of a div element:

<div>User-Controlled Data</div>

Requires different escaping than if you insert that user-controlled data into the id attribute of the div element.

A list of contexts and escaping rules can be found in the OWASP XSS Prevention Cheat Sheet

For the most simple case (inserting user-controlled data into the text of an element), you can use jQuery's .text method or mw.html.escape().