CROSS-SITE SCRIPTING (XSS)XSS persists because context, parser, and framework edges are complex. Treat every user-influenced string as untrusted until it is strictly encoded for the exact sink and guarded by runtime policy (CSP/Trusted Types).
- Reflected, stored, and DOM-based XSS across web/mobile/desktop shells
- Multi-context injections: HTML, attribute, URL, JS, CSS, SVG/MathML, Markdown, PDF
- Framework-specific sinks (React/Vue/Angular/Svelte), template engines, and SSR/ISR
- CSP/Trusted Types interactions, bypasses, and gadget-based execution
1. Identify sources (URL/query/hash/referrer, postMessage, storage, WebSocket, service worker messages, server JSON) and trace to sinks.
2. Classify sink context: HTML node, attribute, URL, script block, event handler, JavaScript eval-like, CSS, SVG foreignObject.
3. Determine current defenses: output encoding, sanitizer, CSP, Trusted Types, DOMPurify config, framework auto-escaping.
4. Craft minimal payloads per context; iterate with encoding/whitespace/casing/DOM mutation variants; confirm with observable side effects beyond alert.
- Server render: templates (Jinja/EJS/Handlebars), SSR frameworks, email/PDF renderers
- Client render: innerHTML/outerHTML/insertAdjacentHTML, template literals, dangerouslySetInnerHTML, v-html, $sce.trustAsHtml, Svelte {@html}
- URL/DOM: location.hash/search, document.referrer, base href, data-* attributes
- Events/handlers: onerror/onload/onfocus/onclick and JS: URL handlers
- Cross-context: postMessage payloads, WebSocket messages, local/sessionStorage, IndexedDB
- File/metadata: image/SVG/XML names and EXIF, office documents processed server/client
- HTML text: encode < > & " '
- Attribute value: encode " ' < > & and ensure attribute quoted; avoid unquoted attributes
- URL/JS URL: encode and validate scheme (allowlist https/mailto/tel); disallow javascript/data
- JS string: escape quotes, backslashes, newlines; prefer JSON.stringify
- CSS: avoid injecting into style; sanitize property names/values; beware url() and expression()
- SVG/MathML: treat as active content; many tags execute via onload or animation events
- Compare responses with/without payload; normalize by length/ETag/digest; observe DOM diffs with MutationObserver
- Time-based userland probes: setTimeout gating to detect execution without visible UI
- Repeat tests across REST, GraphQL, WebSocket, SSE, Service Workers, and background sync; protections diverge per channel
- Sources: location.* (hash/search), document.referrer, postMessage, storage, service worker messages
- Sinks: innerHTML/outerHTML/insertAdjacentHTML, document.write, setAttribute, setTimeout/setInterval with strings, eval/Function, new Worker with blob URLs
- Example vulnerable pattern:
{% raw %}
const q = new URLSearchParams(location.search).get('q');
results.innerHTML = `
${q}
`;
{% endraw %}
Exploit: {% raw %}?q={% endraw %}
- Leverage parser repairs to morph safe-looking markup into executable code (e.g., noscript, malformed tags)
- Payloads:
{% raw %}
- Primary sink: dangerouslySetInnerHTML; secondary: setting event handlers or URLs from untrusted input
- Bypass patterns: unsanitized HTML through libraries; custom renderers using innerHTML under the hood
- Defense: avoid dangerouslySetInnerHTML; sanitize with strict DOMPurify profile; treat href/src as data, not HTML
- Sink: v-html and dynamic attribute bindings; server-side rendering hydration mismatches
- Defense: avoid v-html with untrusted input; sanitize strictly; ensure hydration does not re-interpret content
- Legacy expression injection (pre-1.6); $sce trust APIs misused to whitelist attacker content
- Defense: never trustAsHtml for untrusted input; use bypassSecurityTrust only for constants
- Sink: {@html} and dynamic attributes
- Defense: never pass untrusted HTML; sanitize or use text nodes
- Markdown renderers often allow HTML passthrough; plugins may re-enable raw HTML
- Sanitize post-render; forbid inline HTML or restrict to safe whitelist; remove dangerous URI schemes
- Most clients strip scripts but allow CSS/remote content; use CSS/URL tricks only if relevant; avoid assuming JS execution
- PDF engines may execute JS in annotations or links; test javascript: in links and submit actions
- SVG/HTML uploads served with text/html or image/svg+xml can execute inline; verify content-type and Content-Disposition: attachment
- Mixed MIME and sniffing bypasses; ensure X-Content-Type-Options: nosniff
- Session/token exfiltration: prefer fetch/XHR over image beacons for reliability; bind unique IDs to correlate victims
- Real-time control: WebSocket C2 that evaluates only a strict command set; avoid eval when demonstrating
- Persistence: service worker registration where allowed; localStorage/script gadget re-injection in single-page apps
- Impact: role hijack, CSRF chaining, internal port scan via fetch, content scraping, credential phishing overlays
1. Provide minimal payload and context (sink type) with before/after DOM or network evidence.
2. Demonstrate cross-browser execution where relevant or explain parser-specific behavior.
3. Show bypass of stated defenses (sanitizer settings, CSP/Trusted Types) with proof.
4. Quantify impact beyond alert: data accessed, action performed, persistence achieved.
- Reflected content safely encoded in the exact context
- CSP with nonces/hashes and no inline/event handlers; Trusted Types enforced on sinks; DOMPurify in strict mode with URI allowlists
- Scriptable contexts disabled (no HTML pass-through, safe URL schemes enforced)
1. Start with context classification, not payload brute force.
2. Use DOM instrumentation to log sink usage; it reveals unexpected flows.
3. Keep a small, curated payload set per context and iterate with encodings.
4. Validate defenses by configuration inspection and negative tests.
5. Prefer impact-driven PoCs (exfiltration, CSRF chain) over alert boxes.
6. Treat SVG/MathML as first-class active content; test separately.
7. Re-run tests under different transports and render paths (SSR vs CSR vs hydration).
8. Test CSP/Trusted Types as features: attempt to violate policy and record the violation reports.
Context + sink decide execution. Encode for the exact context, verify at runtime with CSP/Trusted Types, and validate every alternative render path. Small payloads with strong evidence beat payload catalogs.