The Three Types
All XSS shares the same root cause (Chapter 1: input treated as code), but it's classified by where the malicious input comes from and how it reaches the page. The three types — Reflected, Stored, and DOM-based — differ in delivery, persistence, and which part of the stack fails. Knowing the type tells you where to look and how to fix it.
Type 1: Reflected XSS
The payload is included in a request (typically a URL parameter), and the server immediately reflects it back in the response without encoding. It isn't stored — it only affects whoever makes that specific request, so the attacker must deliver the crafted URL to the victim (a link in an email, a message, a malicious ad).
When the victim clicks the link, the server echoes the script into the response and the victim's browser runs it. Reflected XSS is the most common type and requires the victim to interact with an attacker-supplied request.
Type 2: Stored XSS
The payload is saved on the server (in a database, comment, profile field, log) and later served to every user who views the affected page — no special link required. This makes it the most dangerous type: one injection can hit thousands of victims automatically.
Every visitor to the comments page executes the attacker's script in their own session, with no action beyond visiting a normal page. Stored XSS on a high-traffic page is how XSS worms spread (Chapter 5).
Type 3: DOM-Based XSS
The vulnerability is entirely client-side: the page's own JavaScript takes attacker-controllable input (from the URL, location.hash, etc.) and writes it into the page through a dangerous "sink" — the malicious data never needs to touch the server.
location.hash, which browsers don't even send to the server) to a client-side sink (innerHTML, eval, document.write), the malicious data may never reach the backend. That means server-side output encoding — the primary defence for reflected/stored — does nothing here. DOM XSS must be fixed in the client JavaScript itself (safe sinks like textContent, Chapter 5). It's also harder to spot in testing, since server logs show nothing unusual.
The Key Split: Server-Side vs Client-Side
| Reflected | Stored | DOM-based | |
|---|---|---|---|
| Payload source | the request (URL/form) | server storage (DB) | client-side (URL/hash/etc.) |
| Where the flaw is | server reflects unescaped | server stores & serves unescaped | client JS writes to a sink |
| Persistent? | No — per request | Yes — affects all viewers | No — per request |
| Needs victim to click a link? | Yes | No — just view the page | Usually yes (crafted URL) |
| Reaches the server? | Yes | Yes | Often No |
| Fixed by server-side encoding? | Yes | Yes | No — fix the client JS |
Severity, Roughly
- Stored — generally highest: persistent, hits all viewers automatically, no per-victim delivery, can self-propagate (worms).
- Reflected — common and serious, but needs the victim to follow an attacker's link, limiting reach per attack.
- DOM-based — severity varies, but dangerous because it bypasses server-side defences and is easy to miss; can be reflected-like or stored-like depending on the source.
These are tendencies, not rules — a reflected XSS on a bank's login page can be more damaging than a stored XSS in an obscure admin note. The next three chapters take each path deeper, starting with the injection contexts that determine how a payload must be shaped.
Hands-On Exercises
For each scenario, classify the XSS type and justify it: (a) a script in a ?q= URL parameter echoed into search results; (b) a script saved as a product review and shown to all shoppers; (c) the page's JS reading location.hash into innerHTML. State for each whether the payload reaches the server.
Explain why server-side output encoding fixes reflected and stored XSS but does nothing for DOM-based XSS. Identify what part of the stack must change to fix a DOM-based bug, and why it can be invisible in server logs.
📄 View solutionArgue why stored XSS is usually rated more severe than reflected XSS, covering persistence, victim reach, and delivery requirements. Then give one realistic case where a reflected XSS would be more damaging than a particular stored XSS.
📄 View solutionChapter 2 Quick Reference
- Reflected — payload in the request, echoed back unescaped by the server; not stored; needs the victim to click an attacker's link
- Stored — payload saved server-side, served to every viewer; most dangerous; can self-propagate (worms)
- DOM-based — client-side JS reads attacker input (URL/hash) into a dangerous sink; often never reaches the server
- Server-side encoding fixes reflected & stored, but does nothing for DOM-based — fix the client JS (safe sinks)
- Classify with two questions: stored vs reflected? and server-rendered vs client-JS insertion?
- Reflected vs DOM look similar (per-request, URL-driven) — distinguish by who inserts unsafely: server vs page's own JS
- Severity (rough): Stored ≥ Reflected ≥/≈ DOM, but context can flip it
- Next chapter: injection contexts — HTML, attribute, JS, URL, CSS — and why each needs different handling