100% Free Forever
AI-Powered Learning
Industry Expert Content
Certificates & Badges
Learn At Your Own Pace
Security

Preventing XSS

Learn the core defenses against Cross-Site Scripting: contextual output encoding, sanitization, and Content Security Policy, plus how they work together.

Injection & InputIntermediate9 min readJul 10, 2026
Analogies

Core XSS Defenses

Preventing XSS starts from a simple principle: any untrusted data rendered into a page must be treated as data, never as executable markup or script, and the correct way to enforce that depends on exactly where the data lands — HTML body text, an HTML attribute, a JavaScript string, a URL, or CSS all require different encoding rules. Modern frameworks like React, Vue, and Angular escape HTML content by default when using their standard templating syntax, which eliminates the majority of accidental XSS bugs, but they all provide an escape hatch (dangerouslySetInnerHTML, v-html, [innerHTML]) that reintroduces risk the moment untrusted content flows through it.

🏏

Cricket analogy: It is like knowing that a batsman must play differently against a yorker than a bouncer — the correct defensive technique depends entirely on where the ball is arriving, just as encoding rules depend on where data lands in the page.

Output Encoding and Sanitization

Contextual output encoding transforms special characters into their safe representations at the exact point of output — for HTML body content, < becomes &lt; and > becomes &gt; so a browser renders them as visible text rather than parsing them as tags; for a JavaScript string context, characters like quotes and backslashes need JavaScript-specific escaping; and for a URL context, values must be percent-encoded. When an application genuinely needs to accept a subset of HTML from users — such as a rich-text comment editor — it must run that content through a well-vetted sanitization library like DOMPurify that parses the HTML into a safe allow-listed structure, rather than attempting to write custom regex-based filtering, which is notoriously easy to bypass.

🏏

Cricket analogy: It is like a stadium announcer reading out a fan's name letter by letter phonetically rather than as a raw audio clip, ensuring nothing hidden in the original recording can slip through as an unintended broadcast.

javascript
// VULNERABLE: raw user content injected directly into the DOM
const comment = getUserComment(); // e.g. <img src=x onerror=alert(document.cookie)>
document.getElementById('comments').innerHTML += comment;

// SAFE: use textContent for plain text, never innerHTML with untrusted data
document.getElementById('comments').textContent += comment;

// SAFE: if rich text is genuinely required, sanitize with a vetted library
import DOMPurify from 'dompurify';
const clean = DOMPurify.sanitize(comment, { ALLOWED_TAGS: ['b', 'i', 'em', 'strong'] });
document.getElementById('comments').innerHTML += clean;

Content Security Policy

Content Security Policy (CSP) is a response header (Content-Security-Policy) that instructs the browser to restrict which sources of scripts, styles, and other resources it will execute or load, acting as a powerful defense-in-depth layer even if an encoding mistake slips through. A strict policy like script-src 'self' blocks inline <script> tags and any script loaded from a domain other than the site's own origin, which neutralizes most XSS payloads outright since attacker-injected <script> tags or onerror handlers simply won't execute; a nonce-based or hash-based policy is preferred over unsafe-inline for sites that still need some inline scripts.

🏏

Cricket analogy: It is like a stadium's security policy that only allows equipment brought in through an officially sealed and approved channel onto the field, so even if someone sneaks a hidden item past the first checkpoint, it still cannot be used in play.

A CSP that includes unsafe-inline or unsafe-eval in script-src provides almost no protection against XSS, since it explicitly permits the very inline script execution that most XSS payloads rely on — always prefer nonces or hashes for necessary inline scripts.

  • Untrusted data must always be treated as data, never as executable markup, using context-appropriate encoding.
  • HTML, JavaScript string, URL, and CSS contexts each require different encoding rules.
  • Modern frameworks escape by default but expose escape hatches like dangerouslySetInnerHTML that reopen risk.
  • Rich text from users must be sanitized with a vetted library like DOMPurify, never custom regex filtering.
  • Content Security Policy restricts which script sources the browser will execute, adding defense in depth.
  • A strict script-src 'self' policy blocks inline scripts and most injected payloads outright.
  • unsafe-inline in a CSP largely defeats its protective purpose against XSS.

Practice what you learned

Was this page helpful?

Topics covered

#Security#WebSecurityOWASPStudyNotes#CyberSecurity#PreventingXSS#Preventing#XSS#Core#Defenses#StudyNotes#SkillVeris