Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

🏠 Back to Blog

XSS prevention

XSS ties untrusted sources (inputs) to unsafe sinks (where data becomes HTML or JS). Defense should cover both front-end and back-end: client checks improve UX and catch honest mistakes, but attackers can bypass the browser with crafted GET/POST requests, so server-side rules are mandatory for stored and reflected XSS.

Front-end

Input validation

Validate before you trust shape or format. Example pattern from the module: regex over an email field in JavaScript (often wired to jQuery selectors) so invalid addresses block submit—same idea as other allowlist/format checks.

Input sanitization (DOM)

DOMPurify (browser build, e.g. purify.min.js) takes untrusted HTML/strings and returns a safe subset for use in the DOM—use it when you must handle HTML-ish input on the client:

let clean = DOMPurify.sanitize(dirty);

It removes or neutralizes dangerous constructs; treat it as sanitization for HTML context, not a substitute for correct output encoding everywhere.

Avoid dangerous sinks and “HTML soup” APIs

Do not place raw user data inside:

  • <script> / <style>
  • Attribute values or tag names built from input
  • HTML comments built from input

Avoid passing untrusted strings into APIs that parse or inject raw HTML:

Native / DOMjQuery (examples)
innerHTML, outerHTML.html(), $.parseHTML()
document.write(), document.writeln().append(), .prepend(), .after(), .before(), .insertAfter(), .insertBefore(), .replaceAll(), .replaceWith()
document.domain (legacy footgun).add() when fed with unsanitized HTML

Prefer text APIs (textContent, .text(), safe templating) or structured bindings that auto-escape.

Back-end

Front-end validation alone failed in the module’s discovery exercise—repeat checks and enforcement on the server.

Input validation

Same allowlist / format ideas: e.g. PHP filter_var(..., FILTER_VALIDATE_EMAIL); reject and do not reflect invalid input. Node can reuse similar validation logic.

Input sanitization

Use vetted libraries for your language when you must accept rich text. The module mentions PHP addslashes() on parameters such as $_GET['email'] as an example of escaping special characters; in practice addslashes is a poor primary XSS defense for HTML. Prefer context-appropriate handling: validate, then encode on output (below), and use dedicated HTML sanitizers when you truly need HTML storage.

Never echo $_GET / $_POST fields straight into responses.

Node can use DOMPurify (e.g. with jsdom in server environments) to sanitize strings before they reach templates.

Output HTML encoding

When you display user data as text in HTML pages, encode metacharacters so the browser treats them as text, not markup:

  • PHP: htmlspecialchars / htmlentities (e.g. htmlentities($_GET['email'])) so < becomes &lt;, etc.
  • Node: libraries such as html-entities (encode('<')'&lt;').

Combine validation, sanitization where HTML is required, and encoding at the sink to cut XSS risk sharply.

Server and platform hardening

  • HTTPS site-wide.
  • Security headers: modern XSS filters are legacy in browsers; focus on Content-Security-Policy (e.g. script-src 'self' to limit script origins), X-Content-Type-Options: nosniff, and related headers.
  • Cookies: HttpOnly (no JS read) and Secure (HTTPS only) to reduce cookie theft impact.
  • WAF — can block obvious injection patterns; not a full substitute for safe code.
  • Frameworks — some stacks ship auto-encoding or XSS helpers (e.g. ASP.NET defaults mentioned in the module).

Closing mindset

Controls reduce risk; they do not prove absence of bugs. Keep testing (manual, automated, code review) the way you practiced in the offensive sections—paired offense and defense is what makes XSS posture credible.