Web Application Security · intermediate · ~12 min

Cross-site scripting (XSS)

Distinguish the XSS types and give the correct context-aware fix.

Overview

XSS runs attacker JS in a victim's browser in the site's origin. Reflected (echoed from a request), stored (persisted, served to all — worst), and DOM-based (client-side sink) are the types. Root cause: untrusted data in an HTML/JS context without correct encoding. Fix: context-aware output encoding + CSP + a vetted sanitizer.

Why it matters

XSS enables session theft, account takeover, and full client-side compromise within the app's origin. It's pervasive, and the correct fix (context-aware encoding, not input blacklisting) is widely misunderstood — a frequent report-quality issue.

Core concepts

Reflected/stored/DOM. Delivery routes; stored is worst. Origin power. Runs as the site: DOM, cookies, requests. Root cause. Unencoded data in a code context. Fix. Output-encode per context; CSP; DOMPurify; auto-escaping frameworks (mind innerHTML).

Lesson

XSS is injecting attacker JavaScript that runs in another user's browser, in the site's origin — so it can read the DOM, steal non-HttpOnly cookies, make requests as the victim, and rewrite the page.

Three types

  • Reflected — payload in a request (e.g. a search term) echoed unescaped into the response. Delivered via a crafted link.
  • Stored — payload saved by the app (a comment, profile field) and served to every viewer. The most dangerous.
  • DOM-based — client-side JS writes attacker-controlled data into the page (innerHTML, etc.) without sanitization; never touches the server.

The root cause

Untrusted data placed into an HTML/JS/attribute/URL context without correct encoding, so the browser parses it as code instead of text.

The fix (context-aware output encoding)

  • Encode on output for the exact context: HTML-entity-encode for HTML body, attribute-encode in attributes, etc. Frameworks that auto-escape (React, modern templating) prevent most XSS — until you reach for dangerouslySetInnerHTML/innerHTML.
  • Content Security Policy (CSP) as defence-in-depth limits what scripts can run.
  • For rich HTML input, use a vetted sanitizer (e.g. DOMPurify), not a homemade blacklist.

Summary

XSS injects script into a victim's session via reflected, stored, or DOM sinks; it stems from placing untrusted data in a code context unencoded. The fix is context-aware output encoding, reinforced by CSP and trusted sanitizers.

Practice with these exercises