How to do it: XSS

Code-first cards showing how browsers render content, where unsafe HTML handling goes wrong, and how safe output blocks it.

Browser safety Rendered preview Defense first
Safe learning mode: this page avoids harmful script examples and stays focused on safe rendering, sanitization, and browser protections.

Card 1 — render text, not raw HTML

Show the core lesson first: untrusted input should be displayed as text, not interpreted as markup.

Preview + Code
comment preview
unsafe: browser may interpret raw markup safe: comment shown as plain text
Bad pattern: render untrusted HTML directly
Good pattern: treat the input as text content only

Safe rendering example

This is the most important fix to teach on the whole page.

// Safe browser rendering
commentBox.textContent = userComment;

// Avoid rendering raw user-controlled HTML directly.

Card 2 — avoid risky DOM patterns

Explain that convenience APIs can become dangerous when untrusted data is passed straight into them.

Preview + Code
DOM review
risky sink: innerHTML safer sink: innerText / textContent lesson: choose APIs that do not interpret markup

Bad versus safer pattern

Use the contrast to make the lesson visually obvious.

// Risky pattern
container.innerHTML = userComment;

// Safer pattern
container.innerText = userComment;
container.textContent = userComment;

Card 3 — sanitize when HTML is unavoidable

If the app truly needs rich text, run it through a trusted sanitizer before display.

Preview + Code
sanitizer preview
incoming comment → sanitizer → safe output dangerous parts stripped or encoded
trusted sanitization step

Sanitization example

Keep this practical with a well-known safe library pattern.

// Example with DOMPurify
const cleanHtml = DOMPurify.sanitize(userComment);
preview.innerHTML = cleanHtml;

// Only do this when rich formatting is really required.

Card 4 — back it up with CSP

Close the lesson with browser policies that reduce the damage if unsafe content slips through.

Preview + Code
browser policy
CSP blocks unexpected script sources output encoding narrows the attack surface defense in depth matters

Defense header example

End on the policy layer teams should enable in production.

# Example response header
Content-Security-Policy: default-src 'self'; script-src 'self'; object-src 'none'

# Best outcome:
- render user input safely
- sanitize rich text only when needed
- add CSP for defense in depth