Application Security
Beginner 18 min read Application Security

Cross-Site Scripting (XSS)

How the browser itself becomes the victim: reflected, stored, and DOM-based XSS, and the output-encoding rules that eradicate the class.

XSSreflectedstoredDOM XSSCSPoutput encodingsame-origin
Introduction

What is this?

XSS is an injection into the browser: attacker-controlled text is rendered as HTML/JS in a user's session, running with the privileges of the honest origin.

Motivation

Why do you need it?

  • XSS converts a stored comment, a search echo, or a URL fragment into full session theft and keystroke capture.
  • It is the sneakiest top-tier bug: the target audience is other users, not the database.
  • Learning the three flavors (reflected, stored, DOM) generalizes to every templating engine you will ever use.
Think about it

Mental model

Imagine

A forum comment box

Think of

A guestbook where a page of <script> is bound in as a page

The book was meant for words; someone pasted instructions the future readers would execute.

Mechanics

How it works

  • Reflected: input is printed unescaped in the response (search echo), attacker lures the victim via the crafted link.
  • Stored: the payload is saved in the app (comment/profile) and rendered to every subsequent visitor — the most damaging.
  • DOM XSS: the client-side JS reads a location/source and writes it into innerHTML without a sink guard.
  • Same-origin policy is the fence; XSS hops it because the code executes within the honest origin's trust boundary.

A stored XSS lifecycle

Inject
Store
Render
Execute
Steal
Real world

In production

  • A SaaS team enforces a 'encode-on-output' rule in every template, so even user-entered HTML is inert by default.
  • A bank deploys a strict CSP that blocks inline scripts, making even a successful injection hard to weaponize.
  • A bug bounty program rewards a report of stored XSS in a syntax highlighter; the fix ships as library pinning + escaping.
Guidelines

Best practices

  • Encode output by context: HTML-encode text nodes, attribute-encode attributes, and JS-encode in script contexts.
  • Prefer safe frameworks/building patterns that auto-escape (React, Vue templates, Rails erb) over innerHTML surgery.
  • Add a strict Content-Security-Policy (no inline scripts, source allow-lists) as depth, not as a replacement for encoding.
  • Treat any sink (innerHTML, document.write, eval) as a code-review checkpoint.
Watch out

Common mistakes

  • Thinking 'I block <script>' is enough — payloads ride in event handlers, img src, and URL schemes.
  • Escaping only the keyword in the middle of the string and leaving the surrounding HTML mechanics unguarded.
  • Trusting 'the sanitizer library' forever without a critical-update process for it.
  • Assuming innerHTML use in a React-incompatible widget escaped review because 'the app is modern'.
When things break

Debugging tips

  • To triage quickly, echo input verbatim in a <textarea> and see whether it 'emerges' raw in the returned HTML.
  • Browser devtools > Sources is your friend: break on the sink (innerHTML) to confirm DOM-XSS path.
  • Test whether a pasted <img onerror=...> executes in a sandboxed viewer with JS disabled before enabling it.
Security

Security considerations

  • Only verify XSS inside your own lab/own accounts; injecting scripts that affect real users is a crime, not a test.
  • A stored XSS is also a data-processing event: keep logs of the test so forensics know your footprint.
  • Pair XSS defense with session hardening (HttpOnly, Secure flags, SameSite) to cap the value of any theft.
Interviews

Interview checkpoint

Practice

Hands-on lab

Reflected, Stored, and DOM XSS in Your Own Juice Shop
50 min · beginner
In progress
Objectives
  • Reflect XSS in Juice Shop's search field on your own instance.
  • Store a payload that fires for later visitors.
  • Apply the encoding fix in a scratch template and confirm it neutralizes the payload.

Reflected, Stored, and DOM XSS in Your Own Juice Shop

50 min · beginner

0/4 steps
0%
sandbox · Linux sandbox
$
Try these commands
Test yourself

Quick quiz

0/2
1

Stored XSS is worse than reflected because...

2

A strict CSP is best described as...

Go deeper

Official documentation