← Recipes

阅读中文版本

Prevent DOM XSS with Trusted Types

Enable Trusted Types, route unsafe sinks through a policy that sanitizes input, and keep string assignment from injecting scripts.

~40 minsecurityxssfrontend

Task

Add Trusted Types enforcement so assigning strings to dangerous DOM sinks cannot inject scripts.

When to use this

User data reaches innerHTML somewhere in the codebase, so a stored value executes as script even with a reasonable CSP.

Prerequisites

  • A Chromium-based browser for enforcement
  • A sanitizer for a default policy

Steps

1. Send the Trusted Types header so unsafe string assignments become type errors.

Expected output: Dangerous sinks reject plain strings.

2. Create a default policy that sanitizes or escapes input before returning trusted HTML.

Expected output: Only sanitized values reach sinks.

3. Replace direct innerHTML assignment with the policy output.

Expected output: The codebase no longer bypasses the policy.

4. Test with a known injection payload to confirm it is neutralized.

curl -sI https://app.example.com | grep -i content-security-policy | grep trusted-types

Expected output: Injection attempts are rendered inert.

Troubleshooting

Problem

Library uses innerHTML internally

Solution

Wrap or patch the library sink, or move to a version that supports Trusted Types.

Problem

Untrusted HTML needed

Solution

Sanitize with a reviewed library inside the default policy; do not relax the header globally.

Limitations

  • Trusted Types is not enforced in all browsers, so it is defense in depth rather than a universal guarantee.
  • A misconfigured default policy that returns unsanitized HTML reintroduces the vulnerability behind a false sense of safety.

Context

Add Trusted Types enforcement so assigning strings to dangerous DOM sinks cannot inject scripts. User data reaches innerHTML somewhere in the codebase, so a stored value executes as script even with a reasonable CSP.

Steps

  1. Send the Trusted Types header so unsafe string assignments become type errors. Expected: Dangerous sinks reject plain strings.
  2. Create a default policy that sanitizes or escapes input before returning trusted HTML. Expected: Only sanitized values reach sinks.
  3. Replace direct innerHTML assignment with the policy output. Expected: The codebase no longer bypasses the policy.
  4. Test with a known injection payload to confirm it is neutralized. Expected: Injection attempts are rendered inert.
// header
Content-Security-Policy: require-trusted-types-for 'script'
// policy
const policy = trustedTypes.createPolicy('default', {
  createHTML: (s) => DOMPurify.sanitize(s),
});
el.innerHTML = policy.createHTML(userInput);

Verify

  • Dangerous sinks reject plain strings.
  • Only sanitized values reach sinks.
  • The codebase no longer bypasses the policy.
  • Injection attempts are rendered inert.

Limits

  • Trusted Types is not enforced in all browsers, so it is defense in depth rather than a universal guarantee.
  • A misconfigured default policy that returns unsanitized HTML reintroduces the vulnerability behind a false sense of safety.