KB — Building — XSS prevention
XSS prevention researched
Every XSS bug is the same shape: attacker-craftable input reaches the page in a context where it can run as code. Encoding closes that gap; CSP is only the backup. This is the mechanics reference behind the security baseline.
Use this when
You are writing custom JavaScript, accepting URL/form/CMS input into a page, rendering author-supplied HTML (WYSIWYG), or reviewing someone else's embed before it ships.
Definition of done
No untrusted value is interpolated into a <script> tag, event handler, or eval()-family sink. Framework auto-encoding is intact and every escape hatch is justified. Any author-supplied HTML passes through DOMPurify. A strict CSP backs it up (see Content Security Policy).
The three shapes of XSS
An XSS attack tricks a site into executing attacker code within its own origin, subverting the same-origin policy. Once it runs it can read and modify all page content and storage, and make authenticated requests as the user — impersonation and data theft. Every variant depends on the same two conditions: the site accepts craftable input, and it puts that input in a page without ensuring it can't run.
| Type | Where it's injected | Mechanism |
|---|---|---|
| Reflected (client-side) | A URL parameter reflected straight into the page | Unsanitized input assigned to an unsafe sink like innerHTML. Fires per crafted link. |
| Stored / persistent | Server-side, during templating | Untrusted input is stored, then served unsanitized to every visitor — the most severe class. |
| DOM-based | Client-side, entirely in the browser | Dangerous Web APIs (innerHTML, document.write(), eval()) called with unsanitized input. Never touches the server, so a WAF can't see it. |
Defense is layered — there is no single fix
OWASP is explicit: no single technique solves XSS. The durable defense stacks three layers, in this order of reliance:
- Let the framework auto-encode. React/JSX and Django escape interpolated values by default. The whole risk is the escape hatches — audit every one: React
dangerouslySetInnerHTML, AngularbypassSecurityTrustAs*, LitunsafeHTML. - Encode by output context wherever you step outside the framework (table below).
- Sanitize HTML you must render — when encoding would break legitimate author markup.
CSP sits underneath all three as a backup that blocks injected scripts even if they reach the page — it is not a substitute for any layer above. See Content Security Policy.
Output encoding by context
The correct escaping differs by where the data lands — encoding for an HTML body is wrong for a JS string or a URL. Match the rule to the context:
| Context | Encoding rule |
|---|---|
| HTML body | Convert & < > to entities |
| HTML attribute | Quote the value; encode all chars in &#xHH; form |
| JavaScript | Only place data in quoted values; encode as \uXXXX |
| CSS | Hex-encode (\XX); only ever in property values |
| URL | Percent-encode parameters only |
Some contexts are effectively unsafe at any encoding. Never interpolate untrusted data into <script> tags, HTML comments, CSS selectors, event handlers (onclick), or eval()/setTimeout(). Prefer safe sinks: .textContent, .setAttribute() with a hardcoded name, createTextNode().
Sanitizing author HTML
When users legitimately author HTML (a WYSIWYG editor, rich CMS field), encoding it would break the formatting they intend. Sanitize instead with DOMPurify, which OWASP endorses:
const clean = DOMPurify.sanitize(dirty);
element.innerHTML = clean;
On Chromium you can go further with Trusted Types, which forces every DOM sink through a vetted policy — turning an entire class of DOM-based XSS into a load-time error rather than a runtime exploit.
Gotchas
WAFs do not catch DOM-based XSS. The payload never reaches the server, so a firewall can't inspect it. Code review of custom JS is non-negotiable regardless of what's in front of the site.
Webflow exports are static. You get no server-side templating, so nonce-based CSP isn't available — encoding and sanitization in any custom embed carry more of the load there. See Embeds & custom code.
OWASP — the reference body
OWASP (the Open Worldwide Application Security Project) is the volunteer non-profit that publishes the field's de-facto standards. When scoping or justifying security work, these are the documents to cite:
- Top 10 — the reference list of the most critical web-app risks; the usual shorthand for "are we covering the basics?"
- ASVS (Application Security Verification Standard) — a checklist to verify against, by assurance level.
- Cheat Sheet Series — the practical how-tos; the XSS Prevention Cheat Sheet is the source for the encoding rules above.
- Juice Shop — an intentionally vulnerable app for training and demos.
Why & sources
Full cited write-up: Web security for client sites, distilled from MDN — Cross-Site Scripting, the OWASP XSS Prevention Cheat Sheet, and OWASP Foundation. For the backup layer see Content Security Policy; for the ship gate see the security baseline.