XSS Defense 2026
XSS still leaks. The defenses.
Output encoding
Cross-site scripting (XSS) has been the most common web vulnerability for two decades and remains one of the most common in 2026. The attack vector is well understood; the defenses are well documented; teams keep introducing the bug because each layer of defense is easy to miss. The fix is layered: output encoding at the framework, Content Security Policy at the browser, input sanitization for the cases where HTML is genuinely needed.
Why output encoding is the primary defense:
- Escape on output.: When user input is rendered into HTML, it gets escaped: less-than becomes <, ampersand becomes &, quote becomes ". Escaped output cannot inject markup, cannot trigger script execution, cannot break out of attribute values. The escape is mechanical and reliable.
- Framework-level enforcement.: Modern frameworks (React, Vue, Angular, Svelte) escape output by default. Curly braces in templates render escaped strings; explicit escape-bypass requires a deliberate API call (dangerouslySetInnerHTML in React; v-html in Vue). The default is safe; the bypass is named to make it obviously risky.
- Default in React, Vue, etc.: Engineers writing modern frontend code get safe output for free. They have to actively work to introduce XSS, which they sometimes do (parsing user content as HTML, using dangerouslySetInnerHTML without thinking). The default safety is what makes modern frameworks materially safer than the jQuery era.
- Server-side rendering equally important.: Output encoding applies equally to server-rendered HTML. Templating engines (Jinja, ERB, Liquid, Handlebars) all default to escaping output. The framework discipline applies whether the rendering is client-side or server-side.
- Context-aware escaping.: The right escape depends on context. Inside an HTML element body: HTML entity encoding. Inside an attribute: HTML entity encoding plus quote handling. Inside a JavaScript context: JavaScript string escaping. Inside a URL: URL encoding. Modern frameworks handle the context automatically; older code does not.
Output encoding is the floor of XSS defense. Most XSS bugs in 2026 happen because someone bypassed the framework's default escaping; the fix is usually to stop bypassing.
CSP
Content Security Policy is the browser-level defense. The CSP header tells the browser which sources are allowed to load scripts, styles, and other resources. Even if the application has an XSS bug, the CSP can prevent the injected script from executing.
- CSP headers limit inline JS execution.: A CSP that disallows inline scripts (no script tags with content; no event handlers in attributes) blocks the most common XSS payload. The injected script tag does not execute; the bug is mitigated even though the underlying flaw exists.
- Allowlist for script sources.: The CSP specifies which domains can host scripts. Scripts loaded from unallowed domains are blocked. This is the defense against compromised CDNs serving malicious code; even a compromised script source cannot run on your site if it is not on the allowlist.
- Nonce-based for legitimate inline scripts.: Some applications genuinely need inline scripts. The nonce-based CSP allows specific inline scripts that match a nonce attribute, while still blocking arbitrary inline content. The legitimate scripts work; the injected ones do not.
- Report-only mode for adoption.: Adding a strict CSP to an existing application can break legitimate functionality. The report-only mode logs violations without blocking; the team analyzes the logs, fixes legitimate issues, then enables enforcement. The migration is incremental.
- Defense in depth, not primary defense.: CSP is a backstop. The primary defense is output encoding; CSP catches the cases where output encoding has a bug. The two layers compound.
CSP is the browser-side defense that catches what application-side defense missed. Modern web applications should ship with a strict CSP; older ones should migrate.
Sanitize input
The third defense applies in the specific case where the application genuinely needs to render user-generated HTML: rich-text editors, comment systems, knowledge-base content. These cases cannot use output encoding (which would render the HTML as text); they must sanitize the HTML to a safe subset.
- DOMPurify for user-generated HTML.: DOMPurify is the dominant client-side and server-side HTML sanitizer. It parses the HTML, removes dangerous elements (script, iframe, object), removes dangerous attributes (event handlers, javascript: URLs), and produces a safe HTML string. The library is widely audited and broadly trusted.
- Allowlist of safe tags and attributes.: Sanitizer configuration specifies what is allowed: paragraph tags, headings, lists, links, images. Everything else is stripped. The allowlist is reviewable; the team explicitly decides what user-generated HTML can include.
- Sanitize at the right layer.: Sanitize on input (before storage), output (before rendering), or both (defense in depth). Each has trade-offs; both layers compound. Input sanitization keeps the database clean; output sanitization protects against bugs that bypassed input sanitization.
- Defense in depth.: Sanitization is the third layer, applied where the first two cannot. Output encoding does not apply to HTML that is meant to render as HTML; CSP cannot inspect content. Sanitization is the layer that handles user-generated HTML specifically.
- Test the sanitizer.: The sanitizer is itself security-critical code. Test it with known XSS payloads. Verify it rejects each one. The tests live in the security test suite and run in CI; sanitizer regressions are caught before they reach production.
Output encoding, Content Security Policy, and HTML sanitization together produce the layered XSS defense that modern web applications need. Nova AI Ops integrates with web application monitoring to surface CSP violations as security events, audits application code for unsafe escape-bypass patterns, and tracks the XSS-defense posture across the application surface so the team has visibility into whether the layers are still in place.