API & Web

DOM-Based XSS: The Client-Side Attack Developers Miss

How DOM manipulation creates XSS vulnerabilities that server-side protections miss

What Is DOM-Based XSS

DOM-based Cross-Site Scripting (DOM XSS) is a client-side injection vulnerability where malicious JavaScript executes entirely within the browser. Unlike reflected or stored XSS, the malicious payload never reaches the server. Instead, the page's own JavaScript reads attacker-controlled data from the browser environment and writes it into the DOM in an unsafe way.

Classified under CWE-79 and OWASP A03:2021 – Injection, DOM XSS exploits the growing complexity of client-side applications. As single-page applications (SPAs) handle more logic in the browser, the attack surface for DOM XSS has expanded significantly.

The defining characteristic of DOM XSS is that the vulnerability exists in the client-side code itself. The server delivers a legitimate page, but the JavaScript on that page processes user input unsafely. This means the attack bypasses server-side sanitization, WAFs, and traditional output encoding because the dangerous operation happens after the server has finished its response.

DOM XSS can steal session tokens, redirect users to phishing pages, inject fake login forms, or modify page content—all without triggering any server-side logging or detection.

Sources and Sinks in DOM XSS

DOM XSS follows a source-to-sink data flow. A source is where attacker-controlled data enters the JavaScript execution context. A sink is a DOM API that executes or renders that data in a dangerous way.

Common Sources

  • document.location, location.hash, location.search – URL components the attacker controls via crafted links
  • document.referrer – the referring page URL
  • window.name – persists across navigations, controllable by the opener
  • postMessage data – messages from other windows or iframes without origin validation
  • document.cookie – when cookies are set by attacker-controlled subdomains

Dangerous Sinks

  • element.innerHTML and element.outerHTML – parse and render HTML including script tags and event handlers
  • document.write() and document.writeln() – inject raw HTML into the document stream
  • eval(), setTimeout(string), new Function() – execute arbitrary JavaScript from strings

When data flows from a source to a sink without sanitization, the attacker's payload executes as trusted JavaScript:

// Source: URL hash (attacker-controlled)
const userInput = window.location.hash.substring(1);

// Sink: innerHTML (renders HTML)
document.getElementById('content').innerHTML = userInput;

// Attack URL: https://example.com/page#<img src=x onerror=alert(document.cookie)>

In React applications, dangerouslySetInnerHTML acts as the equivalent sink, bypassing React's default escaping.

Why Server-Side Protections Fail

Traditional security measures target server-side vulnerabilities. DOM XSS bypasses all of them because the payload never appears in an HTTP request to the server:

  • Web Application Firewalls (WAFs) inspect HTTP requests and responses. DOM XSS payloads in URL fragments (#) are never sent to the server, so WAFs never see them.
  • Server-side output encoding escapes data before rendering HTML responses. DOM XSS occurs after the server response is delivered, in client-side JavaScript that manipulates the DOM directly.
  • Server-side input validation sanitizes request parameters. URL fragments, postMessage data, and window.name are never part of the server request.
  • Server logs do not capture DOM XSS attacks. The fragment identifier is stripped by the browser before sending the request, making these attacks invisible to monitoring.

This invisibility makes DOM XSS particularly dangerous in compliance-sensitive environments. Attacks leave no server-side evidence, complicating forensic analysis and incident response. The only effective defense is analyzing client-side JavaScript for unsafe source-to-sink data flows before the code reaches production.

How CodeSlick Detects DOM XSS

CodeSlick identifies DOM XSS patterns in JavaScript and TypeScript by detecting unsafe sink usage with dynamic input:

  • innerHTML and outerHTML assignments with template literals, string concatenation, or variable input—static strings and sanitized values (e.g., DOMPurify) are excluded to reduce false positives
  • document.write() and document.writeln() calls with dynamic content
  • React dangerouslySetInnerHTML with unsanitized data and unsafe href attributes containing javascript: protocols
  • Missing Content-Security-Policy headers that would mitigate XSS impact

All findings include CWE-79 classification, CVSS severity scoring, and OWASP A03 mapping. CodeSlick's AI-powered fix engine generates properly escaped alternatives using textContent, DOMPurify sanitization, or framework-appropriate patterns.

Scan your JavaScript and TypeScript code for DOM XSS vulnerabilities in under 3 seconds.

Frequently Asked Questions

Related Guides

DOM-Based XSS: The Client-Side Attack Developers Miss | CodeSlick Security Scanner