XSS: Cross-Site Scripting and Defenses

intermediate
10 min

XSS: Cross-Site Scripting and Defenses

Cross-site scripting (XSS) is what happens when an attacker's content is delivered to other users' browsers and executed as script. Where SQL injection attacks your database, XSS attacks your users: their sessions, their data, their trust in your site. This lesson explains the three flavors of XSS and the layered defenses that neutralize them.

What XSS Can Do

Script running in your page's origin can read and exfiltrate anything the page can: form contents, personal data, tokens in JavaScript-accessible storage. It can rewrite the page to show fake login forms, perform actions as the logged-in user, or quietly redirect visitors. Because the script runs under your domain, the browser's same-origin policy offers no protection; the call is coming from inside the house.

The Three Types

  • Stored XSS: malicious content is saved on the server (a comment, a profile name) and served to every visitor. Highest impact because it spreads automatically.
  • Reflected XSS: input from the request (a search term, a URL parameter) is echoed into the response without encoding; victims are lured into clicking a crafted link.
  • DOM-based XSS: the vulnerability lives entirely in client-side JavaScript, for example code that writes location.hash into innerHTML. The server never sees the payload.

Defense 1: Output Encoding by Context

The core rule: encode data at the moment you put it into a page, using the encoding of that context. HTML body, HTML attributes, JavaScript strings and URLs each have different rules. In HTML, < must become &lt; so it can never open a tag.

Modern frameworks do this by default. React, Vue and Angular escape interpolated values automatically; the danger lives in the deliberate bypasses: dangerouslySetInnerHTML, v-html, bypassSecurityTrustHtml. Treat every use of those as requiring a security review.

javascript
// SAFE: use textContent, never innerHTML, for untrusted data function renderComment(commentText) { const p = document.createElement('p'); p.textContent = commentText; // rendered as text, never parsed as HTML document.querySelector('#comments').appendChild(p); } // If you must render user-authored HTML, sanitize with an allowlist first: // const clean = DOMPurify.sanitize(dirtyHtml);

When users legitimately need rich text, sanitize with a maintained allowlist library such as DOMPurify rather than writing your own regex filter; hand-rolled filters are bypassed constantly.

Defense 2: Content Security Policy

A Content Security Policy (CSP) header tells the browser which sources of script are legitimate, so even injected markup cannot execute:

Content-Security-Policy: default-src 'self'; script-src 'self'; object-src 'none'; base-uri 'self'

A strict CSP that disallows inline scripts and limits script sources turns many exploitable XSS bugs into harmless text. Start with Content-Security-Policy-Report-Only to observe what would break, then enforce. CSP is a backstop, not a substitute for encoding.

Defense 3: Harden the Cookies

Session cookies flagged HttpOnly cannot be read by JavaScript at all, so even successful XSS cannot simply steal them. Add Secure and SameSite while you are there. Prefer HttpOnly cookies over localStorage for session tokens for exactly this reason.

Defense 4: Validate and Limit Input

Input validation is supporting cast: enforce types, lengths and formats on arrival. A username limited to letters, digits and underscores has no room for markup. But remember data can enter from APIs, imports and older records, so output encoding remains the non-negotiable layer.

DOM XSS: Watch Your Sinks

Audit client-side code for dangerous sinks receiving attacker-influenced sources: innerHTML, outerHTML, document.write, eval, setTimeout with strings, and jQuery's $(html). Replace with textContent, setAttribute, and templating that escapes. The Trusted Types browser API can enforce this policy mechanically in supporting browsers.

Key Takeaways

  • XSS executes attacker script in your users' browsers under your site's identity.
  • Encode output for its context; let framework auto-escaping work and audit every bypass.
  • Sanitize rich text with DOMPurify-style allowlists, never homemade regexes.
  • Deploy CSP as a backstop and HttpOnly cookies to protect sessions.
  • Hunt DOM sinks like innerHTML and eval in client code.

Next lesson: CSRF and Clickjacking Defenses, two attacks that abuse the browser's helpfulness rather than injecting code.

XSS: Cross-Site Scripting and Defenses - Cyber Security | CodeYourCraft | CodeYourCraft