high Web Vulnerabilities

DOM-Based Cross-Site Scripting (XSS)

Client-side JavaScript writes untrusted data into the DOM, executing attacker scripts without the server ever seeing the payload.

Severity
high
Time to Fix
4-12 hours
Difficulty
hard
OWASP
A03:2021 — Injection

What is DOM-Based Cross-Site Scripting (XSS)?

DOM-based XSS happens entirely in the browser. JavaScript reads attacker-controlled "sources" — location.hash, location.search, document.referrer, postMessage events, window.name — and passes them into dangerous "sinks": innerHTML, document.write, eval, setTimeout with strings, or jQuery's \$().html(). Because the malicious data never travels to the server, server-side logs see nothing, and WAFs that inspect requests have nothing to match — the payload can live after the # in the URL, which browsers do not even send to servers. Single-page applications with client-side routing are the natural habitat: hash-based routing, query-string handling, and dynamic rendering multiply the source-to-sink paths.

Why It Matters to Your Business

The impact is identical to other XSS — session theft, keylogging, actions performed as the victim, trusted-domain phishing — but detection is harder, so DOM XSS survives longer in production. Payloads delivered via the URL fragment bypass server logs entirely, meaning incidents are often discovered late. For SPAs, one vulnerable sink can affect the whole application because the attacker controls the data before, during, and after client-side rendering.

How to Fix It

1. Map your sources and sinks. Dangerous sources: location.hash/search, document.referrer, postMessage, window.name. Dangerous sinks: innerHTML, outerHTML, insertAdjacentHTML, document.write, eval, new Function, setTimeout/setInterval with strings, element.setAttribute('href'|'src', ...), location assignments.

2. Prefer safe APIs — for rendering text:
  el.textContent = userInput;      // SAFE
  el.innerHTML = userInput;        // DANGEROUS

3. When HTML must be rendered, sanitize with DOMPurify:
  el.innerHTML = DOMPurify.sanitize(userInput);

4. Never pass untrusted strings to eval / new Function / setTimeout-as-string.

5. Framework notes: React JSX and Angular {{ }} escape by default — the risk is dangerouslySetInnerHTML, v-html, [innerHTML]. jQuery: $(el).text() not $(el).html().

6. Enforce Trusted Types (Content-Security-Policy: require-trusted-types-for 'script') — the browser then blocks any sink call with a plain string, catching what reviews miss.

7. Validate postMessage origins: always check event.origin before using event.data.

Frequently Asked Questions

Why does my WAF not catch DOM-based XSS?

Because the attack never reaches the WAF. The payload can live in the URL fragment (#...) which browsers do not transmit to the server at all — the malicious data flows from the URL to JavaScript to the DOM entirely inside the victim's browser.

What are sources and sinks in DOM XSS?

Sources are places attacker data can enter: location.hash, search parameters, document.referrer, postMessage. Sinks are APIs that execute or render: innerHTML, eval, document.write. DOM XSS = an unvalidated path from a source to a sink.

How do I test my application for DOM XSS?

Use client-side analysis (headless scanning catches source-to-sink paths — our DOM scanners do this), plus code review of every innerHTML/eval call, plus Trusted Types in report-only mode to surface violations in production safely.

Technical Classification

OWASP CategoryA03:2021 — Injection
CWE IDCWE-79: Improper Neutralization of Input During Web Page Generation
Detected Bydom_hygiene, client_path_taint, dalfox_xss scanner(s)
Severity Levelhigh

How 2MNY Security Can Help

Check if your website has this vulnerability

Our dom_hygiene, client_path_taint, dalfox_xss scanner checks for this issue automatically.

Scan My Website Free

Related Vulnerabilities