
Cross-Site Scripting (XSS) is a well-known vulnerability that allows attackers to inject malicious scripts into web pages. One of the most sneaky forms of this attack is DOM-based XSS—where the malicious code is executed directly in the browser, manipulating the Document Object Model (DOM), often without the server even knowing. Let’s dive deep into the basics of DOM XSS, how it works, and how to protect your applications.
🌐 What is the DOM?
The Document Object Model (DOM) is the live, interactive structure of a web page. Think of it as a map or blueprint of everything that appears on a website: text, images, form elements, links, etc.
JavaScript uses the DOM to dynamically modify the structure and content of a webpage while it’s being viewed. This allows the page to be interactive, updating the content in response to user actions or events.
🏗️ DOM Structure Example:
Consider a basic webpage with a simple heading:
<!DOCTYPE html> <html> <head> <title>Simple Page</title> </head> <body> <h1 id="greeting">Hello, User!</h1> </body> </html>
In the browser’s memory, this page is represented by a tree-like structure called the DOM tree. It looks something like this:
Document └── html ├── head │ └── title └── body └── h1 #greeting
Each element (like the <h1>
tag) is a node in the tree, and JavaScript can interact with it to update the content, change styles, or modify attributes.
🔄 How JavaScript Updates the DOM
JavaScript can manipulate the DOM using various methods, allowing developers to dynamically update the content of a webpage. This is often done with functions like document.getElementById(), innerHTML
, textContent
, etc.
Example 1: Changing Content Dynamically
Here’s an example where JavaScript updates the content of a page:
<!DOCTYPE html> <html> <body> <h1 id="greeting">Hello, User!</h1> <script> document.getElementById("greeting").innerHTML = "Welcome, John!"; </script> </body> </html>
In this example, the content of the <h1>
element is updated from "Hello, User!"
to “Welcome, John!” using JavaScript.
⚠️ DOM XSS: What Is It?
DOM-based XSS occurs when an attacker injects malicious JavaScript code directly into the DOM via untrusted user input. This could be through URL parameters, web storage, or even user inputs. Once this input is injected into the DOM and executed, it can lead to malicious actions, such as stealing cookies or redirecting users to phishing sites.
🛑 Example of DOM XSS
Let’s take a look at an example where an attacker can inject malicious content into the DOM:
<!DOCTYPE html> <html> <body> <h1 id="greeting">Hello, User!</h1> <script> const userInput = new URLSearchParams(window.location.search).get("name"); document.getElementById("greeting").innerHTML = `Welcome, ${userInput}`; </script> </body> </html>
If a user accesses the page with this URL:
http://example.com/?name=<script>alert('XSS')</script>
The page will display the alert XSS
, which is a clear sign of the DOM XSS vulnerability.
🔑 Key Concepts: Source & Sink
1️⃣ Source (📥 Entry Point)
A source is the location where untrusted data enters your JavaScript code. It could be user input, URL parameters, web storage, or any place where an attacker can control the content.
Examples of Sources:
- URL parameters (e.g., window.location.search)
- Form inputs (
<input>
, <textarea>) - Web storage (
localStorage
, sessionStorage)
Example:
const userInput = new URLSearchParams(window.location.search).get("message");
2️⃣ Sink (💥 Dangerous Output Location)
A sink is the place in your JavaScript code where untrusted data is written to the DOM. If untrusted data is written to a sink without proper sanitization, it can execute malicious scripts.
Examples of Sinks:
- innerHTML
- document.write()
- eval()
- jQuery.html()
Example:
document.getElementById("greeting").innerHTML = userInput; // Dangerous sink!
💥 Vulnerable Page on PinewoodStore: Giftcard.html
Let’s look at a real-world example of a DOM XSS vulnerability in the PinewoodStore application. Specifically, we’re focusing on the Giftcard.html page, which is vulnerable to this attack.
🚨 Vulnerable Code:
The vulnerable code on the Giftcard.html page looks like this:
<!-- SOURCE: Unsafe URL parameter extraction --> <script> const messageParam = new URLSearchParams(location.search).get('message'); // SINK: Dangerous DOM insertion (using jQuery's .html) $('#successMessage').html(messageParam); // XSS HERE! </script>
💣 How the Attack Works
- An attacker crafts a malicious URL: http://pinewoodstore.com/getGiftCard?message=<script>alert(‘Hacked’)</script>
- A victim clicks on the link (this could be via email, social media, etc.).
- The page executes the malicious JavaScript in the victim’s browser, triggering the alert
Hacked
.
🚨 Impact
The impact of DOM XSS can be severe:
- Cookie theft: Attackers can steal session cookies and hijack accounts.
- Keylogging: Capture keystrokes from the victim’s browser.
- Phishing attacks: Redirect victims to fake login pages or malicious websites.
🛡️ How to Protect Your Site from DOM XSS
1. Use .textContent instead of .innerHTML
Always prefer .textContent over .innerHTML to avoid executing malicious scripts:
document.getElementById("greeting").textContent = userInput;
2. Sanitize Input with DOMPurify
Use libraries like DOMPurify to sanitize input and remove malicious scripts:
document.getElementById("greeting").innerHTML = DOMPurify.sanitize(userInput);
3. Implement Content Security Policy (CSP)
A Content Security Policy helps prevent unauthorized scripts from running:
<meta http-equiv="Content-Security-Policy" content="script-src 'self'">
4. Validate and Sanitize URL Parameters
Always validate and sanitize URL parameters before using them in your code:
const userInput = new URL(location).searchParams.get("message");
5. Use Automated Scanning Tools
Tools like Burp Suite and OWASP ZAP can automatically scan for DOM XSS vulnerabilities, making it easier to detect and fix them.
🧑💻 Final Thoughts
DOM XSS is a stealthy attack that’s challenging to detect but devastating once exploited. By understanding the DOM, identifying sources and sinks, and applying the right defenses, you can protect your application and users from this type of attack.