DOM-based XSS

DOM-based XSS occurs when attacker-controlled data reaches an unsafe DOM operation through client-side JavaScript. The vulnerability exists in the browser-side data flow rather than requiring the server to reflect or store the payload.

DOM-based XSS is a client-side injection vulnerability. A typical flow has an attacker-controlled source, such as `location.hash`, `location.search`, or `document.referrer`, followed by JavaScript processing and an unsafe DOM sink.

The key issue is how data is interpreted at the sink. Assigning attacker-controlled text to `innerHTML`, for example, causes the browser to parse the value as HTML. By contrast, assigning untrusted text to `textContent` treats it as text rather than markup.

A secure design traces untrusted data from its source to its destination and avoids HTML-interpreting sinks when markup is unnecessary. If HTML must be created dynamically, the application should use an appropriate trusted rendering mechanism or a carefully configured sanitizer rather than concatenating raw input into HTML.

DOM-based XSS can be present even when the server never receives the malicious value. For example, JavaScript can read a fragment from the URL and immediately insert it into the page, making the browser itself responsible for the vulnerable data flow.

For interviews, distinguish DOM-based XSS from reflected and stored XSS by identifying where the dangerous transformation occurs. The important question is whether client-side code takes attacker-controlled data and sends it to an unsafe DOM sink.

Key points

Interview questions

What is DOM-based XSS?

DOM-based XSS occurs when client-side JavaScript takes attacker-controlled data and places it into a dangerous DOM context. Common sources include URL components, while common sinks include HTML-parsing APIs. The defining feature is that the vulnerable transformation happens in browser-side code. It does not require the server to store or reflect the payload.

Why is `innerHTML` dangerous with untrusted input?

`innerHTML` treats its assigned string as HTML markup, so untrusted content can introduce elements or event-handler attributes that the browser interprets. If the value is attacker-controlled, this can create an XSS condition. For plain text, `textContent` is preferable because it does not parse the assigned value as HTML. If dynamic HTML is required, the application needs an appropriate trusted rendering or sanitization strategy.

How would you find a DOM XSS vulnerability during a code review?

I would identify sources of attacker-controlled data and then trace where those values flow through the application. I would pay particular attention to HTML-parsing or script-sensitive sinks and to transformations that may make a value dangerous again. I would also inspect whether framework-specific escaping guarantees actually apply to the destination context. Finally, I would verify the fix by testing the complete source-to-sink path.

What is the difference between DOM-based, reflected, and stored XSS?

The distinction is primarily about where attacker-controlled data travels and where the vulnerable interpretation occurs. Reflected XSS commonly involves the server putting request data into a response immediately, while stored XSS involves persistence before later rendering. DOM-based XSS is caused by browser-side JavaScript processing data and reaching an unsafe DOM sink. These categories can overlap in a real application, so the actual source-to-sink flow matters more than the label alone.

How would you fix a DOM XSS vulnerability?

First, I would determine whether the HTML interpretation is actually necessary. If only text is needed, I would replace the unsafe sink with an API such as `textContent` or an equivalent framework-safe mechanism. If dynamic HTML is required, I would use a well-defined trusted rendering path or an appropriately configured sanitizer and ensure the resulting value is safe for its exact context. I would also add regression tests covering the relevant source-to-sink path and review related sinks for the same pattern.