CodePark/ questions / XSS: sources, sinks & sanitizing
START PRACTICINGPRACTICE
Frontend question · Frontend security › XSS: sources, sinks & sanitizing

You must show user-written HTML, such as rich-text comments. How do you do it safely with DOMPurify?

import DOMPurify from "dompurify";

function Comment({ html }) {
  const clean = DOMPurify.sanitize(html, {
    ALLOWED_TAGS: ["b", "i", "em", "strong", "a", "p", "ul", "li"],
    ALLOWED_ATTR: ["href"],
  });
  return <div dangerouslySetInnerHTML={{ __html: clean }} />;
}

Run the HTML through an allowlist sanitizer right before you insert it. DOMPurify parses the HTML with the browser's own parser. It removes everything dangerous, such as <script> tags, event attributes like onerror, and javascript: URLs. It keeps the safe tags.

Rules for using it well:

  • Allow as little as possible. Use ALLOWED_TAGS and ALLOWED_ATTR to list only what your editor produces.
  • Sanitize last. Don't change the string after sanitizing. Joining strings or running a markdown or template step afterwards can create new dangerous HTML. If you render markdown, sanitize the HTML that the markdown library outputs.
  • Sanitize where you render. Server-side cleaning on save is a good extra layer. But the check right before insertion protects you even if old or imported data is dirty.
  • Keep DOMPurify updated. Researchers find bypasses, and new versions fix them.
  • Ask if you need HTML at all. Plain text with textContent, or markdown with a small allowed set, is much safer.

DOMPurify also works with Trusted Types. The RETURN_TRUSTED_TYPE option makes it return a TrustedHTML object instead of a string.

  • Use an allowlist sanitizer like DOMPurify right before inserting HTML.
  • DOMPurify removes scripts, event handler attributes and javascript: URLs.
  • Allow only the tags and attributes your feature needs.
  • Never change the HTML after sanitizing it.
  • Keep the sanitizer updated, and prefer plain text when HTML isn't needed.