DOM XSS

In an earlier blog post  we looked at a basic introduction to Cross-Site Scripting or “XSS”, and how to prevent it when developing in-house web applications.

In that article we mainly focused on outlining how a basic form of XSS known as “Reflected XSS” works, because this is the simplest form to explain, as well as the form that is most commonly encountered, and the simplest for an attacker to exploit.

We also mentioned a variant known as “DOM XSS” in that article but didn’t dig further into exactly how it worked – that’s what we’re going to look at today. Strictly speaking, this form of XSS is more broadly referred to as “Client-Side XSS”.

 

 

 

First up – what’s the “DOM”?

 

“DOM” stands for “Document Object Model” and although you may not have heard of it, it underpins how every web browser interprets and renders web pages that are received from the server. It represents a web page as a document so that client-side scripting languages can change the structure, style, and content. Typically, web pages are transmitted and presented as HTML content, along with instructions on how associated static content should be rendered and displayed. However, this representation can all be manipulated and changed through client-side code talking to the programming interface exposed by the DOM.

 

The DOM is represented as an object-orientated tree structure of nested elements known as nodes. Each node represents an individual page element, and the DOM allows unique identification of each element in the tree via a dot-delimited and hierarchical “address”. For example a form may have the name “userDetails” and be addressable therefore as “document.userDetails”.

 

 

 

 

How does the DOM relate to XSS?

 

As a brief refresher, both reflected and stored XSS are ultimately “executed” on the client-side but rely on flaws in the handling of input or output processed by the server. That is, there is an input handler on the server somewhere that fails to correctly maintain the separation between executable code and user-provided data under certain conditions (a process that is typically ensured via measures such as robust input validation. The flawed input handler allows an attacker to send malicious data that causes the server to return or “reflect” unsanitized responses back to the client containing the attacker’s input as injected code, which the user’s browser then faithfully executes.

In server-side XSS, input is sent to the web server via HTTP requests using HTTP verbs (methods) such as GET or POST, with the user-supplied input appearing as included (or “reflected”) output unchanged within the response. The malicious input is said to be “reflected” back as part of the output code provided by the server to the client to be executed client-side. However, this same “input-output” mechanism exists client-side, albeit in a very different form, as we will outline below.

As we saw above in relation to the DOM, with every element on the page having an “address” or unique name, client-side code (such as JavaScript) can therefore address a page element programmatically and interact with it. Within a page JavaScript can:

 

  • add, change, and remove any of the HTML elements and attributes;
  • change any of the CSS styles;
  • react to all the existing events; and
  • create new events.

 

An “event” is what allows webpages to be truly dynamic: it is something that either the browser does, or that the user does (such as “the webpage has finished loading”, or “the user has clicked on an HTML button”) that, via an event listener permits the launching of associated JavaScript code.

 

 

 

 

 

What can go wrong with the handling of input client side?

 

However, flaws also exist in client-side handling of input, allowing it to write code into the DOM in an unsafe and unintended manner. Technically, we say that DOM XSS exists if an attacker is able to place data (rather than content) into a source (some input they control and are intended to control) so that it is propagated to a sink (the reflection point that eventually executes, or helps with execution of, the malicious JavaScript injected through the source) that fails to maintain the separation between executable code and client-provided data and therefore permits the execution of arbitrary JavaScript. Sources for the data can include the DOM itself, but also less commonly considered sources such as the result of a returned AJAX “”Asynchronous JavaScript and XML”) call to the server.

 

 

The flawed code is ultimately still written by in-house developers, but the difference is that the code is not executed (or any input evaluated) on the server-side; the flawed code is simply handed to the user’s browser as a static resource for it to evaluate and make use of, and the XSS occurs when a specific event fires within the page running in the client’s browser, using user-provided input.

Just as when code is evaluated server-side, the possibility exists for any input passed to client-side code to “break out” of its intended containment as user data and be evaluated as code instead. Specific functions such as eval(), setTimeout() and setInterval() tend to be particularly vulnerable sinks if passed data directly from user input without careful handling or sanitisation within the JavaScript code.

 

 

What is an example of DOM XSS in practice?

 

Let’s suppose that the following code is used to create a form to let the user select their preferred language. A default language is also provided in the query string, as the parameter “default”.

 

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

 

The page is invoked with a URL such as:

 

http://www.example.com/page.html?default=French

 

A DOM Based XSS attack against this page can be accomplished by sending the following URL to a victim:

 

http://www.example.com/page.html?default=<script>alert(document.cookie)</script>

 

When the victim clicks on this link, the browser sends a request for:

 

/page.html?default=<script>alert(document.cookie)</script>

 

to www.example.com. The server responds with the page containing the above JavaScript code. The browser creates a DOM object for the page, in which the document.location object contains the string:

 

http://www.example.com/page.html?default=<script>alert(document.cookie)</script>

 

The original JavaScript code in the page does not expect the default parameter to contain HTML markup, and as such it simply decodes and writes it into the page (DOM) at runtime. The browser then renders the resulting page and executes the attacker’s script:

 

alert(document.cookie)

 

Note that the HTTP response sent from the server does not contain the attacker’s payload. This payload manifests itself at the client-side script at runtime, when a flawed script accesses the DOM variable document.location and assumes it is not malicious.

 

 

 

How prevalent is DOM XSS?

 

DOM-based XSS is becoming increasingly critical, both in terms of frequency of exploit and impact as more and more web applications implement their UI code using a new wave of fronted web technologies in a paradigm known as Single Page Applications (“SPAs”). These interact with the user by dynamically rewriting the current web page and fetching new data from the web server in the background, instead of the default method of a web browser loading entire new pages. Because SPAs rely more heavily on JavaScript as in integral underpinning of their navigation and operation than traditional web applications (which rely more heavily on server-side processing under a request-response cycle) they are more susceptible to DOM XSS vulnerabilities.

The graph below shows global Google search volumes for the term ‘Single Page Application.’ Note the sharp rise in popularity from around 2011.

 

 

How can I prevent DOM XSS?

 

Preventing DOM XSS requires sensible precautions be taken during each step of the software development lifecycle (SDLC) to prevent a robust series of measures that together reduce the overall risk:

  • During the planning, analysis and design phases, architects should ensure that the risks of DOM XSS are properly understood, and the potential measures that can be taken to manage that risk understood. During this phase, appropriate libraries and frameworks are generally selected that can help support safe code construction.
  • During the development phase, it is important that developers understand how to use appropriate escaping, encoding, and rigorous use of “safe” functions. This will depend greatly on appropriate developer training and education, as well as on the use of appropriate frameworks, libraries and methods selected during the earlier phase, in order to de-risk the introduction of potential DOM XSS vulnerabilities. The Open Web Application Security Project (OWASP) DOM Based XSS Prevention Cheat Sheet is a good resource that provides a list of best practice rules and guidelines to follow (https://cheatsheetseries.owasp.org/cheatsheets/DOM_based_XSS_Prevention_Cheat_Sheet.html). However, the most essential guidance is not too dissimilar to that for how to prevent other forms of XSS – escaping and encoding input received from untrusted sources before passing it for execution – albeit within the context of client-side rather than server-side code. Since it is very difficult to ensure that escaping and encoding are performed correctly if performed on an individual instance basis (i.e., remembering to do it every time), the use of “safe” libraries and frameworks can assist greatly in reducing the cognitive load and dependence on developers “getting it right every time”.
  • During the testing phase, teams can combine careful manual peer code review/code audit with the use of linters and static analysers within CI/CD pipelines in order to enforce coding standards and catch unsafe method usage, or instances of input being handled directly rather than through approved libraries and frameworks. Testing can also incorporate web application vulnerability scanning using a tool such as AppCheck, to catch any vulnerabilities that may not have been caught in other checks. Since AppCheck can be integrated into Continuous Integration (CI/CD) pipelines, it can scan code in staging or other pre-production instances, highlighting vulnerabilities before they ever make it to production.
  • During the implementation phase, teams can put into place further global or site-wide protection using a sensibly defined Content-Security Policy (CSP) that allows restrictions on all script execution and can, if configured correctly, reduce the likelihood of some forms of DOM XSS.

 

 

 

How can AppCheck Help?

 

AppCheck help you with providing assurance in your entire organisation’s security footprint. AppCheck performs comprehensive checks for a massive range of web application vulnerabilities – including DOM XSS vulnerabilities and client-side code weaknesses – from first principle to detect vulnerabilities in in-house application code.

 

The AppCheck web application vulnerability scanner includes an advanced and custom DOM-based XSS scanning module that can detect DOM XSS vulnerabilities. It does this by having a full native understanding of JavaScript code, including Single Page Applications (SPAs) and renders and evaluates them in the exact same way as a user web browser does.

The AppCheck Vulnerability Analysis Engine provides detailed rationale behind each finding including a custom narrative to explain the detection methodology, verbose technical detail, and proof of concept evidence through safe exploitation.

 

 

 

 

About AppCheck

AppCheck is a software security vendor based in the UK, offering a leading security scanning platform that automates the discovery of security flaws within organisations websites, applications, network, and cloud infrastructure. AppCheck are authorized by the Common Vulnerabilities and Exposures (CVE) Program as a CVE Numbering Authority (CNA).

 

 

Additional Information

As always, if you require any more information on this topic or want to see what unexpected vulnerabilities AppCheck can pick up in your website and applications then please get in contact with us: info@appcheck-ng.com

 

Get started with Appcheck

No software to download or install.
Contact us or call us 0113 887 8380

Start your free trial