There’s no denying the role that JavaScript has played in making web applications the sleek, interactive, online experiences that we know and love today. This powerful scripting language brought interactivity and animation to the web. But with great power comes great responsibility. Cross-site-scripting (XSS) remains a persistent stalwart among the OWASP Top 10. Malicious JavaScript code hidden in the DOM is all it takes to compromise a user’s data and avoid conventional, server-side centered cross-site-scripting (XSS) scanning techniques.
What is DOM-based XSS?
The Document Object Model (DOM) lets web developers dictate through HTML source code how a user’s web browser should display a web page. DOM-based XSS attacks seek to exploit the DOM in a simple two step process:
- Create a Source: Inject a malicious script into a property found to be suceptible to DOM-based XSS attacks. Common injection vectors include document.url, document.location, and document.referrer objects.
- Exploit the Sink: A sink is the point in the data flow where the browser will execute the malicious JavaScript code hidden in the DOM. Common sinks include document.write, setTimeout, and setInterval.
For a typical example of how a DOM-based XSS attack is executed, it’s suggested that you read DOM XSS: An Explanationof DOM-based Cross-Site Scripting.
DOM-based Cross-site Scripting
DOM-based Cross-site Scripting (from now on called DOM XSS) is a very particular variant of the Cross-site Scripting family and in web application development is generally considered the amalgamation of the following:
- The Document Object Model (DOM) – Acting as a standard way to represent HTML objects (i.e. <div></div>) in a hierarchical manner.
- Cross-site Scripting (XSS) – A specific Web Application vulnerability.
Wherein DOM XSS uses the DOM to exploit XSS by relying on the insecure handling of user input on a static or dynamic HTML page. This is particularly common when applications leverage common JavaScript function calls such as:
document.baseURI
To build some part of the page, without sanitizing the return value. That said, the purpose of this article isn’t to explain DOM XSS fully, but rather how to defend against it.
Example
On one of our Test HTML5 applications there is a DOM XSS vulnerability that can be exploited via the following payload:
http://testhtml5.vulnweb.com/#/redir?url=javascript:alert("DOM XSS on: " + document.domain)
Which would look something like the following image – an informational message with a simple alert. Note how the payload is stored in the GET request, making it suitable for Social Engineering attacks.
The above is quite trivial but is an easy way to prove our point. The payload is embedded in the URI and so can be easily made part of a phishing campaign. The payload can be manipulated to deface the target application using prompt stating, “Your session has expired. Please insert your password to refresh your session”. A simple yet effective way to harvest passwords.
If we dig deeper, we find out that the #redir route is being executed by another file, called redir.html found here. If you view the source of the page, the gist of the code is as follows:
<script> var redirUrl = decodeURIComponent(window.location.hash.slice(window.location.hash.indexOf("?url=")+5)); if (redirUrl) window.location = redirUrl; </script>
Essentially we are exploiting the window.location.hash source which is evaluated in an HTML Element Sink.
Remediation
Detecting DOM XSS is difficult using purely server-side detection (i.e. HTTP requests), which is why providers like Acunetix leverages DeepScan to do it. These payloads are never sent to the server due to being behind an HTML fragment (everything behind the # symbol).
As a result, the root issue is in the code (i.e. JavaScript) that is in the page. This means that you should always sanitize user input, irrespective of whether or not it is client-side.
If you have to use user-input at any point in time on your page, always use it in the context of literal “text” and never as potential code. Avoid methods such as:
document.innerHTML
And instead use safer functions when using user input like so:
document.innerText document.textContent
This will treat the previous payload we showed as just text and nothing else. Additionally, avoid using user input entirely especially ones that can affect:
The above three properties may manipulate the DOM thus leading to such vulnerabilities. Keep in mind that DOM XSS and XSS are not mutually exclusive, meaning that your application can most definitely be vulnerable to both XSS and DOM XSS—even though XSS is normally found in dynamic pages and DOM XSS in static ones. The good news is that if user input is handled properly as a foundational level (e.g. your framework), then you should be able to mitigate all XSS-based vulnerabilities.
For a great cheat sheet on how to prevent DOM XSS entirely, I would highly recommend going over the OWASP DOM based XSS Prevention Cheat Sheet.
Conclusion
Lastly, leveraging a Web Application Scanner such as DeepScan by Acunetix will greatly increase both the speed and accuracy to which developers and security professionals can detect vulnerabilities like DOM XSS and fix them and thousands more.