FlawOpen

flawopen.com/XSS/Types of XSS

Reflected vs. stored vs. DOM-based XSS: what's the difference?

Reference page
Short answer

Reflected, stored, and DOM-based describe different paths from untrusted input to an unsafe browser context. They can overlap: the key questions are where the value enters, whether it persists, which code renders it, and which user context receives it.

The three types

Reflected XSS — the payload lives in the request

A request value is included in the response without context-appropriate encoding, and the browser interprets it as markup or script. An attacker may send a crafted link to a victim, but the underlying flaw is the unsafe rendering of request data. Fix the output context at the rendering sink.

Stored XSS — the payload lives in the database

Untrusted content is saved and later rendered unsafely to other users, for example in a comment or profile field. Each visitor who loads the affected content may receive the same unsafe browser context until the stored value or rendering path is corrected.

DOM-based XSS — client-side code sends data to an unsafe sink

Client-side JavaScript reads an untrusted source (such as location.hash, a query parameter, or data returned by the server) and sends it to an unsafe DOM sink such as innerHTML. The value may never reach the server, but that is not required: DOM-based XSS describes the client-side source-to-sink path and can overlap with reflected or stored delivery. Fix the client-side data flow.

Why the distinction matters for fixing it

Fix XSS at the rendering sink with context-appropriate output encoding or a safe DOM API. Reflected and stored XSS often involve server-rendered output, but client-side rendering can also create the unsafe sink. For DOM-based XSS, prefer textContent over innerHTML for text; if rich HTML is required, use a maintained sanitizer with an explicit policy. Do not treat server-side encoding as a substitute for reviewing client-side source-to-sink flows.

These labels can overlap

Reflected and stored describe how input reaches a page and whether it persists. DOM-based describes a client-side source-to-sink path. They are not mutually exclusive: persisted profile text inserted by client code into innerHTML can be both stored and DOM-based XSS.

DOM-based XSS: unsafe sink and text-only rendering

UNSAFE — parse untrusted text as HTML
const value = new URLSearchParams(location.search).get("q") ?? "";
target.innerHTML = value;
SAFER FOR TEXT — render as text
const value = new URLSearchParams(location.search).get("q") ?? "";
target.textContent = value;

References

See also Cross-Site Scripting (XSS, CWE-79): How It Works and How to Prevent It in Every Language — flawopen.com Android addJavascriptInterface Security Risks Angular XSS Protection: Safe Defaults and Bypass APIs Content-Type Sniffing XSS: MIME Types and nosniff CSP vs. Output Escaping: Do You Need Both? Is React's dangerouslySetInnerHTML Safe? Is DOMPurify Enough for Safe innerHTML?