Lesson 8 / 25

Cross-Site Scripting (XSS)

Encode output, use safe APIs, add CSP.

Untrusted data in a page

XSS lets attackers run JavaScript in other users' browsers by injecting it into pages: stored (saved in a comment), reflected (in a URL parameter) or DOM-based (client code writes untrusted data into the page). Defences: rely on frameworks that auto-escape output (React, Angular, Django templates), avoid raw HTML sinks (innerHTML, dangerouslySetInnerHTML, v-html) or sanitise with a vetted library such as DOMPurify, encode for the right context (HTML, attribute, URL, JavaScript), and add a Content Security Policy as a second layer.

Unsafe and safe DOM updates plus a CSP header

JavaScript and an HTTP header.

// VULNERABLE: interprets the comment as HTML, so <img src=x onerror=...> runs code
commentEl.innerHTML = comment.text;

// SAFE: treat it as text
commentEl.textContent = comment.text;

// If rich HTML is required, sanitise with a vetted library
commentEl.innerHTML = DOMPurify.sanitize(comment.html);

// HTTP response header limiting where scripts may come from
// Content-Security-Policy: default-src 'self'; script-src 'self'; object-src 'none'; base-uri 'self'

Set HttpOnly on session cookies

HttpOnly prevents scripts from reading the session cookie, limiting what an XSS payload can steal.

Quick check: Which DOM property safely inserts untrusted text?

  • outerHTML
  • innerHTML
  • textContent
  • document.write
Answer

textContent — Text is never parsed as HTML.