So, what is DOM XSS?
For over ten years Cross-Site Scripting (XSS) has been included within the OWASP’s Top 10 Web Application Security Risks. Although the issue is extremely well covered online, it is sometimes overlooked by application owners, meaning the vulnerability persists in even some of the most sophisticated modern-day web applications. Despite being heavily documented, as penetration testers, we see this on a common basis taking several forms.
In the modern era of web applications, more responsibility is passed to client-side scripts to deal with processing data. In turn, new techniques have arisen and the threat landscape for such issues is shifting. As mentioned, the coverage of XSS through online outlets is second to none, being one of the most well-covered web application security issues to date. However, it is surprising how many people still do not fully understand the concept surrounding ‘DOM (Document Object Model) Based’ or Client-Side XSS.
Before we go into the details of the issue, there are few key points which must be discussed:
Client Side Vs. Server Side
One key concept that must be understood to fully grasp the issue, is the difference between the terms ‘client side’ and ‘server side’ within this context. This blog does not fully discuss differences between client side and server side functions as a whole, but more specifically code that may provide functionality to the respective sides.
Client side functions are events which happen within the user’s browser and require no interaction from the server itself. JavaScript being the most common client side scripting language, used in over 95% of web applications (upwards of 1.5 Billion) can provide a wealth of functionality when creating new code. Although there are other languages/libraries to facilitate this kind of functionality (such as VBScript, jQuery, Typescript, etc.), for the purposes of this blog we will focus on JavaScript, however the concept remains the same throughout. Client side languages are used for a multitude of reasons such as lightening server load, speeding up response times and facilitating single page functionality.
Server side functions are events that happen on the remote server, such as processing, and storing and retrieving data. Here, popular languages such as PHP, C# and more recently Python and GO allow the application to interact with datastores and facilitate more complex functionality.
The key takeaway from this is that client side functions happen within the browser and do not require a change to the HTTP request in order to modify what is seen on the page. Thus, anything which happens within the client side, for example processing a payload, does not depend on the server. This concept is the key to understanding DOM based XSS.
Sources and Sinks
As the names states, DOM based XSS is facilitated and exploited through the use of dangerous client side scripts. The way in which this is illustrated, for the purposes of this issue, is through sources and sinks.
A source is exactly what you would expect, the source of the user controllable input. There are a multitude of sources that could pass malicious code to enable such an attack within JavaScript, this could be anything from ‘document.getElementById(‘ID’).value’ to ‘location.search’. It should be stated that on their own, using such methods does not constitute a vulnerability, which is the cause of much confusion surrounding this issue.
A sink is the output where the value passed from the source is directly injected back into the DOM. This could be a method such as ‘innerHTML’ or ‘document.write’. Again, there are numerous ways in which data could be outputted, potentially presenting a significant opportunity for attacks. However, it is the whole process flow that allows such an attack vector to exist.
Putting into practice what we have discussed above, consider the following. This application makes a GET request when searching, however the user input is processed using JavaScript:

The code above takes a search string passed via a GET parameter in the URL. Although the page has to reload in order for this to take effect, the payload is never processed by the server and relies on the client side completely. See the following, which breaks-down the above code to demonstrate the previously mentioned parts:
Window.location.search – this acts as the source. The source of the vulnerability, which is user controllable and passed to the rest of the code.
Document.write – this is the sink, which writes anything supplied by the source to the page, for example, HTML markup or JavaScript Syntax.
For instance, if a user passed the value ‘<img src=test onerror=alert(1)>’ through the search parameter, the value would be passed to document.write and an alert would be seen on screen. This is similar to a reflected XSS attack, in the way that unsafe input is directly displayed back onto the users screen. However, as previously mentioned the payload is never processed by the server itself.
As you can see from what is demonstrated above, the concept does not differ from reflected or stored XSS. However, what is key to remember is that the payload is never processed by the server. Although GET requests are processed by the server, the response does not facilitate the attack. However the client side writes the payload to the document executing a similar affect.
One key point to remember for testers, to really pin this issue down, as a rule of thumb if you use the browser to ‘view the page source’ and cannot see the payload, the flaw can be considered DOM based. This is due to the JavaScript modifying the DOM to receive the intended output of the used payload. As in the example above, see the difference in the DOM and server response below.


As demonstrated in the screenshots, the payload can be seen in the DOM but not in the server response. Thus, classifying such a vector as DOM based.
What does this mean in terms of consequence? As with any other type of XSS, the consequences could be low severity issues to a potentially critical issue. It all depends on circumstance. If the attack vector only affects the current user in page and could not be leveraged by an external attacker, the risk could be considered low severity. However, if the DOM based vector allowed for stealing admin users’ cookies, then this could be considered a high severity vector. The point is, a vector being considered DOM based is no less severe than traditional reflected or stored XSS vulnerabilities.
In terms of remediation, although specifics are not possible as there are a plethora of source and sink combinations, developers should think about how user input is handled. Ask the questions, does a user really need to search for special characters? Does the query string need to be treated as HTML when displayed on the page? It all falls down to circumstance and planning. Before writing a function, all things should be considered, for example, how user input is going to be used. In the case above, if careful planning had been undertaken, the source could be used in a perfectly safe manor with a different sink. If the developer had replaced document.write, with a simple ID selection and text.content statement, unsafe input would be reflected in a safe manner.
As mentioned previously, this issue is one of the most well covered out there. The issue stems from a lack of planning and understanding of how user input may be utilised in a malicious way. The responsibility of dealing with such a task is on developers themselves, not relying on third party libraries or other people’s code to keep them safe. A full understanding of what XSS actually is will help ensure weaknesses cannot be leveraged via utilisation of dangerous user input vectors.
To summarise this whole issue into few sentences, remember the following; the flaw stems from client side code, as such the payload is never processed by the server, making this a difficult concept to understand. However, the key to ensuring your application is safe from such issues is understanding the problem and how a user could potentially escape intended functionality to cause harm. In turn, allowing pre-emptive techniques to be put into place, dealing with any potentially dangerous payloads.
References
https://www.cloudflare.com/learning/serverless/glossary/client-side-vs-server
https://portswigger.net/web-security/cross-site-scripting/dom-based