Three Flavors of XSS¶

Run the Three Flavors of XSS MicroSim Fullscreen
You can include this MicroSim on your own website with the following iframe:
<iframe src="https://dmccreary.github.io/cybersecurity/sims/xss-three-flavors/main.html" height="542" width="100%" scrolling="no"></iframe>
About this MicroSim¶
Cross-site scripting (XSS) is one bug class with three meaningfully different shapes, and the difference is where the attacker's payload lives and whether the server ever touches it. This infographic lays the three subtypes side by side as data-flow columns so the contrast is visible at a glance.
Stored XSS (left) saves the payload on the server — one injected comment is
then served to every later visitor, so the attacker needs no further action.
Reflected XSS (middle) carries the payload in a request and the server echoes
it straight back into the response, so it only fires when a victim clicks the
crafted link. DOM-based XSS (right) is the sneaky one: the payload rides in
the URL fragment (#...), which browsers never send to the server, and a
vulnerable piece of client-side JavaScript reads it from a source
(location.hash) and writes it into a dangerous sink (innerHTML) — so the
attack can be completely invisible to server logs.
Every box is color-coded: blue for a legitimate flow, amber for attacker-controlled data, and a red outline on the box where the script finally executes in the victim's browser. Hover (or tap on touch devices) any step to read exactly what happens and why it is dangerous. The layout is responsive — below about 860px wide the three columns stack vertically while keeping every tooltip.
Lesson Plan¶
Learning objective (Bloom — Understand / compare): Students can compare the data flow of stored, reflected, and DOM-based XSS, state where the payload lives in each, and explain why DOM-based XSS can evade server-side logging.
Suggested classroom use: Walk the three columns left to right, hovering each
step. Pause at the "Server" boxes and ask which subtype's server is innocent
(DOM-based — it served an unmodified page). Connect the red "executes" box in all
three to the single shared defense: contextual output encoding and avoiding
dangerous sinks like innerHTML.
Discussion questions:
- A defender greps the web server's access logs for
<script>and finds nothing, yet users are being compromised. Which XSS subtype best explains this, and why? - Stored XSS needs no victim action while reflected XSS does. What is it about the data flow that creates that difference?
- Output encoding fixes stored and reflected XSS at the server. Why is it not enough for DOM-based XSS, and what changes?
References¶
- Cross-site scripting (Wikipedia)
- Types of XSS — OWASP
- DOM-based XSS — OWASP
- XSS Prevention Cheat Sheet — OWASP
Specification¶
The full specification below is extracted from Chapter 5: "Software Vulnerabilities and Secure Coding".
Type: infographic-svg
**sim-id:** xss-three-flavors<br/>
**Library:** Static SVG with hover tooltips<br/>
**Status:** Specified
A three-column infographic, one column per XSS subtype, each showing the data flow as a sequence of actors and arrows.
**Column 1: Stored XSS**
Actors top-to-bottom: Attacker → Server (with database icon) → Victim browser
Arrows:
1. Attacker → Server: "POST /comment payload=`<script>...</script>`"
2. Server stores payload in database
3. Victim → Server: "GET /comments"
4. Server → Victim: "page contains `<script>...</script>`"
5. Victim browser executes script
Caption: "One injection compromises every visitor."
**Column 2: Reflected XSS**
Actors: Attacker → Victim → Server → Victim browser
Arrows:
1. Attacker → Victim: "Phishing email with crafted link `?q=<script>...</script>`"
2. Victim → Server: "GET /search?q=`<script>...</script>`"
3. Server → Victim: "Search results page reflects `q` into HTML without escaping"
4. Victim browser executes script
Caption: "Requires the victim to click the crafted URL."
**Column 3: DOM-based XSS**
Actors: Attacker → Victim → Browser only (no server interaction)
Arrows:
1. Attacker → Victim: "Crafted URL `https://site/#payload=<script>...</script>`"
2. Victim browser fetches static page
3. Page's JavaScript reads `location.hash`
4. Page's JavaScript writes hash into `innerHTML` → script executes
Caption: "Server never sees the payload — invisible to server logs."
Color: cybersecurity blue for legitimate flows, alert amber `#ffa000` for attacker-controlled data, red outline on the "executes script" box.
Responsive: columns stack vertically below 900px.
Implementation: Static SVG with consistent layout, `<title>` tooltips on each step.