Cross-Site Scripting (XSS)
XSS injects attacker-controlled script into a page viewed by others. Output encoding, safe DOM APIs, and CSP limit execution, while input validation alone is insufficient.
Definition
Cross-Site Scripting is a vulnerability where untrusted data is interpreted as executable code in a victim's browser. In reflected XSS the payload comes from the current request; in stored XSS it is persisted and served to later visitors; in DOM XSS the client-side code writes untrusted data into a dangerous sink such as innerHTML. The impact includes session theft, action forgery, credential phishing, and data exfiltration.
Context and scope
XSS applies wherever applications render user or third-party content, including comments, profiles, search results, markdown previews, and admin dashboards. It is prevented primarily at output time by encoding data for the correct context and by avoiding dangerous APIs, with CSP as defense in depth.
Common misconceptions
- Input validation alone prevents XSS — the same value may be safe in one context and dangerous in another; output encoding is required
- React escapes everything — dangerouslySetInnerHTML and unvalidated URLs bypass escaping
- CSP makes XSS impossible — CSP reduces impact and blocks many payloads, but misconfigurations and allowed origins can still permit execution
Anti-patterns
- Building HTML with string concatenation and inserting it with innerHTML
- Putting untrusted values into href, src, or style attributes without protocol validation
- Storing HTML from users and rendering it without sanitization
Related concepts
Related examples
Authoritative source
Three delivery paths
Reflected XSS echoes a request value into the response. Stored XSS persists the payload and serves it to later users. DOM XSS occurs when client code moves untrusted data into a dangerous sink without server involvement.
Prevent at the sink
// ❌ HTML injection
container.innerHTML = `<p>${userInput}</p>`;
// ✅ textContent treats the value as text
const p = document.createElement('p');
p.textContent = userInput;
container.append(p);
When HTML is genuinely required, sanitize with a maintained allowlist library and encode for the exact context. Validate URL schemes before assigning to href or src; allow only https:, mailto:, and explicitly approved schemes.
Defense in depth
- Encode output for HTML, attributes, JavaScript, CSS, and URLs.
- Use safe framework APIs and avoid raw HTML sinks.
- Set a restrictive Content Security Policy with nonces or hashes.
- Set cookies
HttpOnlyso script cannot read session cookies. - Sanitize markdown and rich text with a vetted library.
When it matters most
Any page that displays content from another user or an external system is a candidate. Compared to CSRF, XSS attacks the user’s trust in the page itself and can read data the user can see; CSRF causes actions without reading responses. Both require context-aware defenses, not a single global filter.
Pitfalls
Blacklist filtering of <script> misses event handlers, SVG, and encoded payloads. Escaping once and then decoding later reopens the hole. Storing unsanitized HTML and relying on the client to clean it shifts the vulnerability to every consumer.