XSS Learning Notes
A practical map of XSS attack surfaces and defense checks, based on PayloadsAllTheThings, OWASP, and MDN.
Testing Method
- Identify the output context first: HTML text, attribute, URL, JavaScript string, CSS, DOM sink, or iframe wrapper.
- Paste context-specific payloads one by one, and record not only whether they execute but which policy blocks them.
- Test delayed surfaces too: admin consoles, notifications, mail, log viewers, previews, and search indexers.
Commonly Missed Surfaces
Removing script tags is not enough if event handlers, SVG onload, autofocus/onfocus, or quote breakouts survive.
href, src, action, and data need protocol allowlists after decoding and normalization.
location, hash, postMessage, or storage values become DOM XSS when they reach innerHTML or string-eval APIs.
ProseMirror, Markdown, and MDX often store one format and render another. Sanitize the final HTML.
Embed safety needs host/protocol allowlists, sandbox, allow attributes, and referrer policy, not just tag filtering.
SVG/HTML uploads and admin review screens are classic delayed stored-XSS surfaces.
Defense Checklist
- Apply output encoding per context, and use a trusted sanitizer only where HTML is truly needed.
- Normalize URLs, then validate protocol and host allowlists.
- Replace DOM sinks with structured APIs such as textContent, setAttribute, and createElement.
- If you use DOMPurify, document and test allowed tags, attributes, and URL policy per product feature.
- Allow only necessary iframe hosts and start with a conservative sandbox.
- Use CSP as a safety net, not as the only layer that stands in for sanitization.
Real Product Questions To Discuss
A ProseMirror editor saved content to a database, then a separate renderer displayed it. Sanitization with DOMPurify helped, but iframe embeds raised a hard question: when users want many providers, is host allowlisting still the right operational model?
Profile fields look small, but they are reused everywhere. A nickname in an attribute or an icon URL through a custom renderer can spread stored XSS into comments, notifications, and admin lists.