DOM-Based XSS: How to Prevent DOM-Based XSS Attack
Introduction
Cross-site scripting (XSS) is one of the most common and dangerous web application vulnerabilities. It allows attackers to inject malicious code into web pages, which can compromise the security and privacy of users, steal sensitive data, hijack sessions, deface websites, and perform other malicious actions. XSS attacks can be classified into three types: reflected, stored, and DOM-based. In this article, we will focus on the third type, which is also known as client-side XSS or type 0 XSS. We will explain what DOM-based XSS is, how it works, how to detect it, and how to prevent it.
What is DOM-Based XSS?
DOM stands for Document Object Model, which is a representation of the structure and content of a web page. The DOM can be manipulated by JavaScript code to dynamically update the web page without reloading it. For example, JavaScript can change the text, images, links, styles, and events of a web page using the DOM API.
DOM-based XSS is a type of XSS attack that exploits the vulnerability in the client-side code that interacts with the DOM. Unlike reflected and stored XSS, which occurs when the server sends malicious code to the browser as part of the HTTP response, DOM-based XSS occurs entirely on the client-side, without any server interaction. The attacker injects malicious code into the web page through a user input or a URL parameter that is processed by the client-side code. The malicious code then executes in the context of the web page and accesses the DOM.
How DOM-Based XSS Works
To understand how DOM-based XSS works, let’s first review how a web browser renders a web page. When a user requests a web page from a web server, the server responds with an HTML document that contains the markup and metadata of the web page. The browser parses the HTML document and creates a DOM tree, which is a hierarchical representation of the elements and attributes of the web page.
The browser then executes any scripts that are embedded or referenced in the HTML document, which can modify the DOM tree dynamically. The browser also applies any style sheets that are linked or embedded in the HTML document, which can change the appearance and layout of the web page. Finally, the browser renders the web page on the screen based on the final state of the DOM tree.
DOM-based XSS exploits the fact that scripts can modify the DOM tree dynamically. An attacker can craft a malicious URL that contains a script or a payload that can alter the DOM tree in a way that introduces or executes malicious code. For example, an attacker can append a query string parameter that contains a script to a legitimate URL, such as:
https://example.com/index.html?name=<script>alert('XSS')</script>
When a user visits this URL, the browser will parse the HTML document and create a DOM tree as usual. However, when it encounters the script element in the query string parameter, it will execute it as part of the web page. This will result in an alert box popping up on the screen with the message ‘XSS’. This is a simple example of DOM-based XSS that demonstrates how an attacker can inject arbitrary code into a web page using client-side scripts.
Common Attack Vectors and Scenarios
There are many ways an attacker can manipulate the DOM tree using client-side scripts. Some of the common attack vectors and scenarios are:
URL:
As we saw in the previous example, an attacker can append a script or a payload to a URL that is used by a script on the web page to access or modify some element or attribute in the DOM tree. For instance, an attacker can use a URL like:
https://example.com/index.html?name=<img src=x onerror=alert('XSS')>
If the web page uses a script like:
document.getElementById('name').innerHTML = location.search.substring(6);
to display the value of the name parameter in an element with id ‘name’, then the attacker’s payload will be executed when the image fails to load.
Document Location:
An attacker can also use document.location or window.location objects to access or modify some element or attribute in the DOM tree. These objects represent the current URL of the web page and can be manipulated by scripts. For example, an attacker can use a URL like:
https://example.com/index.html#<script>alert('XSS')</script>
If the web page uses a script like:
document.write(location.hash.substring(1));
to write the value of the hash fragment to the document, then the attacker’s script will be executed.
Document Referrer:
An attacker can also use document.referrer object to access or modify some element or attribute in the DOM tree. This object represents the URL of the previous web page from which the user navigated to the current web page. For example, an attacker can use a URL like:
https://attacker.com/index.html?name=<script>alert('XSS')</script>
and then navigate to a web page that uses a script like:
document.write(document.referrer.substring(document.referrer.indexOf('?')+1));
to write the value of the query string parameter to the document. This will result in the execution of the attacker’s script.
Document Cookie:
An attacker can also use document.cookie object to access or modify some element or attribute in the DOM tree. This object represents the cookies that are associated with the current web page. For example, an attacker can use a URL like:
https://example.com/index.html?name=<script>document.cookie='XSS=1'</script>
If the web page uses a script like:
document.write(document.cookie);
to write the value of the cookie to the document, then the attacker’s script will be executed and set a cookie with name ‘XSS’ and value ‘1’.
Document Write:
An attacker can also use document.write or document.writeln methods to write arbitrary HTML or script code to the document. For example, an attacker can use a URL like:
https://example.com/index.html?name=<script>document.write('<img src=x onerror=alert("XSS")>')</script>
If the web page uses a script like:
document.write(location.search.substring(6));
to write the value of the name parameter to the document, then the attacker’s script will be executed and write an image element with an onerror attribute that contains a script.
DOM Properties:
An attacker can also use various DOM properties that can be accessed or modified by scripts to introduce or execute malicious code. For example, an attacker can use a URL like:
https://example.com/index.html?name=<script>document.body.innerHTML='<img src=x onerror=alert("XSS")>'</script>
If the web page uses a script like:
document.body.innerHTML = location.search.substring(6);
to set the innerHTML property of the body element to the value of the name parameter, then the attacker’s script will be executed and set the innerHTML property of the body element to an image element with an onerror attribute that contains a script.
These are just some of the common attack vectors and scenarios for DOM-based XSS. There are many other ways an attacker can exploit this vulnerability, depending on the logic and functionality of the web application and the browser.
How to Test for DOM-Based XSS
Testing for DOM-based XSS is not as straightforward as testing for reflected or stored XSS, because there is no server-side feedback or response that indicates the presence of the vulnerability. However, there are some methods and tools that can help in identifying and exploiting DOM-based XSS.
One of the simplest methods is to manually inspect the source code of the web page and look for any scripts that use the following sources of user input or external data:
- URL parameters (e.g., location.search, location.hash)
- Document properties (e.g., document.location, document.referrer, document.cookie)
- Browser properties (e.g., window.name, window.opener)
- DOM elements and attributes (e.g., document.getElementById, element.innerHTML, element.src)
If any of these sources are used without proper validation or encoding to access or modify some element or attribute in the DOM tree, then there is a potential for DOM-based XSS. For example, if the source code contains a script like:
document.getElementById('name').innerHTML = location.search.substring(6);
then it is possible to inject arbitrary HTML or script code into the element with id ‘name’ by appending it to the URL as a query string parameter.
Another method is to use automated tools that can scan the web page and detect any scripts that are vulnerable to DOM-based XSS. Some of these tools are:
- [DOMinator]: A Firefox add-on that analyzes JavaScript code and identifies sinks (where user input or external data is used) and sources (where user input or external data originates) that are relevant for DOM-based XSS.
- [DOM XSS Scanner]: A Chrome extension that scans web pages for DOM-based XSS vulnerabilities using various heuristics and techniques.
- [XSStrike]: A Python tool that fuzzes web pages for XSS vulnerabilities using various payloads and vectors.
- [ZAP]: A popular web application security testing tool that has a feature called Ajax Spider that crawls web pages and executes JavaScript code to find DOM-based XSS vulnerabilities.
Detecting DOM-Based XSS Vulnerabilities
DOM-based XSS vulnerabilities are harder to detect than reflected and stored XSS vulnerabilities because they do not involve any server-side code or HTTP response. However, there are some common signs and symptoms that can indicate the presence of a DOM-based XSS vulnerability. These include:
- User input or URL parameters that are directly used to modify the DOM without proper validation or encoding. For example, if a web page uses
document.write
orinnerHTML
to insert user input or URL parameters into the web page, it may be vulnerable to DOM-based XSS. - User input or URL parameters that are used to construct JavaScript code or expressions that are then executed by
eval
,setTimeout
,setInterval
,Function
, or other similar methods. For example, if a web page useseval
to execute user input or URL parameters as JavaScript code, it may be vulnerable to DOM-based XSS. - User input or URL parameters that are used to load external resources such as scripts, stylesheets, images, or iframes from untrusted sources. For example, if a web page uses
document.createElement
orsetAttribute
to create script elements with user input or URL parameters as the source attribute, it may be vulnerable to DOM-based XSS.
To detect DOM-based XSS vulnerabilities more effectively and accurately, it is recommended to use Automated Vulnerability Scanning tools that can analyze the client-side code and identify potential injection points. Some examples of such tools are:
- [OWASP ZAP]: A free and open-source web application security scanner that can perform active and passive scanning for various types of web application vulnerabilities, including DOM-based XSS.
- [Acunetix]: A commercial web application security scanner that can perform comprehensive crawling and scanning for various types of web application vulnerabilities, including DOM-based XSS.
- [Burp Suite]: A commercial web application security testing tool that can perform proxying, crawling, scanning, fuzzing, and other advanced techniques for various types of web application vulnerabilities, including DOM-based XSS.
These tools can help you find and exploit DOM-based XSS vulnerabilities on your website and provide you with detailed reports and recommendations on how to fix them.
Real-World Examples
DOM-based XSS attacks are not as common as reflected or stored XSS attacks, but they still pose a serious threat to website security. Here are some notable cases of DOM-based XSS attacks that have affected popular websites and applications:
- British Airways: In 2018, British Airways was attacked by Magecart, a high-profile hacker group famous for credit card skimming attacks. The group exploited a DOM-based XSS vulnerability in a JavaScript library called Feedify, which was used on the British Airway website. Attackers modified the script to send customer data to a malicious server, which used a domain name similar to British Airways. The fake server had an SSL certificate, so users believed they were purchasing from a secure server. They succeeded in performing credit card skimming on 380,000 booking transactions before the breach was discovered.
- Fortnite: In 2019, the popular multiplayer game experienced a DOM-based XSS vulnerability that over 200 million users. A retired, unsecured page went unnoticed by Fortnite developers. The page had a DOM-based XSS vulnerability that allowed attackers to gain unauthorized access to the data of all Fortnite users. Attackers could have used XSS, in combination with an insecure single sign-on (SSO) vulnerability, to redirect users to a fake login page. This would allow them to steal virtual currency within the game, and record player conversations, as reconnaissance for future attacks. Check Point discovered the attack and notified Fortnite, but it is unknown if the vulnerability was exploited by attackers in the interim.
- eBay In late 2015 and early 2016, eBay had a severe DOM-based XSS vulnerability. The website used a “URL” parameter that redirected users to different pages on the platform, but the value of the parameter was not validated. This allowed attackers to inject malicious code into a page. The vulnerability enabled attackers to gain full access to eBay seller accounts, sell products at a discount, and steal payment details.
The impact of these attacks on businesses and users was significant. They resulted in financial losses, data breaches, identity theft, reputation damage, and legal consequences. These examples show that DOM-based XSS attacks can affect any website or application that uses client-side code to manipulate the DOM and that they can have devastating consequences.
How to Prevent DOM-Based XSS
DOM-based XSS attacks can be prevented by following some best practices for secure coding and web development. These include:
- Sanitizing user input: User input or URL parameters that are used to modify the DOM or execute JavaScript code should be sanitized before use. This means removing or encoding any characters that can be interpreted as HTML or JavaScript syntax, such as
<
,>
,"
,'
,&
,;
, etc. For example, if a web page usesdocument.write
to insert user input into the web page, it should useencodeURI
orencodeURIComponent
to encode the user input first. - Using security libraries: Instead of writing your own code to sanitize user input or manipulate the DOM, you can use existing security libraries that provide these functionalities. For example, you can use [DOMPurify], a JavaScript library that sanitizes HTML and prevents XSS attacks. You can also use [jQuery], a JavaScript library that simplifies DOM manipulation and provides methods that automatically encode user input before inserting it into the web page.
- Content Security Policy (CSP) implementation: CSP is a web security mechanism that allows you to control what resources can be loaded or executed on your web page. You can use CSP to whitelist trusted sources for scripts, stylesheets, images, iframes, and other resources, and block any untrusted sources. You can also use CSP to disable unsafe methods such as
eval
,document.write
,innerHTML
, etc., that can execute arbitrary code from user input or URL parameters.
By following these steps, you can protect your website from DOM-based XSS attacks and ensure a safe and secure browsing experience for your users.
Testing and Validation
Testing and validation are essential steps in ensuring the security of your website and web applications. You should test your website and web applications for XSS vulnerabilities before deploying them to production, and validate them regularly after deployment. Testing and validation can help you identify and fix any vulnerabilities that may exist or arise over time. There are two main methods of testing and validation: automated testing tools and manual code review.
- Automated testing tools: Automated testing tools are software applications that can perform various types of security tests on your website and web applications, such as crawling, scanning, fuzzing, exploiting, etc. Automated testing tools can help you find XSS vulnerabilities faster and more efficiently than manual testing. However, automated testing tools may not be able to detect all types of XSS vulnerabilities, especially those that require complex logic or user interaction. Therefore, automated testing tools should be complemented by manual code review.
- Manual code review: Manual code review is the process of examining the source code of your website and web applications by human experts. Manual code review can help you find XSS vulnerabilities that automated testing tools may miss or overlook. Manual code review can also help you understand the root cause of the vulnerabilities and provide recommendations on how to fix them. However, manual code review may be time-consuming and costly, depending on the size and complexity of your website and web applications. Therefore, manual code review should be prioritized based on the risk level of your website and web applications.
By using both automated testing tools and manual code review, you can ensure a comprehensive testing and validation process for your website and web applications.
Case Study: Successful Vulnerability Mitigation
To illustrate how DOM-based XSS vulnerabilities can be successfully mitigated, let us look at a real-life scenario involving Google Maps. In 2014, Google Maps had a DOM-based XSS vulnerability that allowed attackers to inject malicious code into the map view by modifying the URL parameters. The vulnerability was discovered by a security researcher named Kushagra Pathak, who reported it to Google through their Vulnerability Reward Program. The vulnerability was fixed by Google within two weeks.
The following is a detailed analysis of the vulnerability and the strategies employed to mitigate it: