Trusted Types API: A Complete Guide to Preventing DOM XSS

Learn how browser-enforced Trusted Types can eliminate entire classes of DOM-based cross-site scripting vulnerabilities in your web applications.

What Are Trusted Types and Why They Matter

DOM-based XSS remains one of the most common and dangerous vulnerabilities in modern web applications. The Trusted Types API provides browser-level enforcement to eliminate entire classes of injection vulnerabilities by requiring developers to explicitly mark data as trusted before passing it to dangerous DOM APIs.

Unlike traditional sanitization approaches that rely on developer discipline, Trusted Types create a structural security boundary where the browser itself enforces the rules--any attempt to bypass the policy results in an immediate runtime error.

The Core Problem

Web browsers provide numerous APIs that accept strings and interpret them as HTML, JavaScript, or URLs:

element.innerHTML = userInput; // HTML sink - dangerous!
document.write(htmlContent); // HTML sink - dangerous!
eval(codeString); // JavaScript sink - dangerous!
script.src = dynamicUrl; // Script URL sink - dangerous!

Traditional defense relies on sanitization libraries like DOMPurify, but this approach has critical weaknesses:

  • Developers must remember to sanitize everywhere
  • Third-party code may bypass sanitization
  • New dangerous APIs can be introduced without review
  • A single missed location creates a vulnerability

Trusted Types solve this by making sanitization mandatory at the browser level. This shift from developer discipline to structural enforcement is fundamental to building secure web applications with web development services that prioritize security from the ground up, and can be enhanced through AI automation workflows that intelligently validate and sanitize content at scale.

How Trusted Types Prevent XSS Attacks

The Policy-Based Security Model

Trusted Types work through a policy-based architecture where developers define policies--named factories that transform untrusted input into trusted objects:

// Create a policy that sanitizes HTML
const sanitizePolicy = trustedTypes.createPolicy('sanitize', {
 createHTML: (input) => DOMPurify.sanitize(input)
});

// Later, use the policy to create trusted HTML
const trusted = sanitizePolicy.createHTML(userContent);
element.innerHTML = trusted; // ✓ This works!
element.innerHTML = userContent; // ✗ This throws an error!

The Three Trusted Types

The API provides three specialized types, each protecting different categories of injection sinks:

TypePurposeProtected Sinks
TrustedHTMLSafe HTML contentinnerHTML, outerHTML, insertAdjacentHTML, document.write
TrustedScriptSafe JavaScript codeeval(), new Function(), setTimeout/setInterval (string)
TrustedScriptURLSafe script URLsscript.src, iframe.srcdoc, Worker constructor

Why Type Safety Matters

Trusted type objects are opaque and non-convertible back to strings--this prevents circumvention:

const trusted = policy.createHTML('<b>Hello</b>');
trusted + '<script>evil()</script>'; // Cannot concatenate!
String(trusted); // Cannot convert!

The browser enforces that only these typed objects can reach protected sinks, creating a complete security boundary that integrates seamlessly with modern JavaScript development practices and complements comprehensive SEO services by ensuring your security implementations don't inadvertently harm search visibility.

Trusted Types Core Components

Understanding the key elements that make up the Trusted Types security model

Policies

Named factories that define how untrusted input is transformed into trusted output. Each policy specifies createHTML, createScript, and/or createScriptURL methods.

TrustedHTML

Represents sanitized HTML that can safely be assigned to DOM properties like innerHTML without risking XSS injection.

TrustedScript

Represents approved JavaScript code that can be executed through eval() or similar sinks under strict control.

TrustedScriptURL

Represents verified script URLs for dynamic script loading, preventing malicious URL injection in script.src.

CSP Integration for Enforcement

The require-trusted-types-for Directive

The require-trusted-types-for CSP directive enables browser enforcement of Trusted Types. When enabled, the browser rejects any string assignment to protected DOM sinks:

Content-Security-Policy: require-trusted-types-for 'script';

With this header set, the following code throws a TypeError:

element.innerHTML = '<p>Hello</p>';
// Uncaught TypeError: Failed to set the 'innerHTML' property
// on 'Element': This document requires 'TrustedHTML' assignment.

The trusted-types Directive

The trusted-types directive whitelists which policies are allowed, preventing malicious code from creating its own policies:

Content-Security-Policy: require-trusted-types-for 'script'; trusted-types sanitize dompurify default;

This configuration:

  • Enables Trusted Types enforcement
  • Allows only named policies: 'sanitize', 'dompurify', and 'default'
  • Blocks any attempt to create unauthorized policies

The Default Policy

A special policy named 'default' can catch string assignments during migration:

trustedTypes.createPolicy('default', {
 createHTML: (input) => DOMPurify.sanitize(input)
});

When the default policy exists, string assignments are automatically passed through it rather than throwing an error--useful for gradual migration while identifying all violation locations. This approach aligns with our security-first development methodology that prioritizes protecting existing codebases while systematically improving security posture through intelligent AI-powered validation.

Complete CSP Header Configuration
1# Full enforcement - strict mode2Content-Security-Policy: 3 require-trusted-types-for 'script';4 trusted-types sanitize-policy user-content-policy;5 6# Report-only mode for testing7Content-Security-Policy-Report-Only:8 require-trusted-types-for 'script';9 trusted-types * 'allow-duplicates';10 11# Development mode - allows any policy with duplicates12Content-Security-Policy:13 require-trusted-types-for 'script';14 trusted-types * 'allow-duplicates';

Implementing Trusted Types in React

The Challenge: dangerouslySetInnerHTML

React's dangerouslySetInnerHTML property is a common source of XSS vulnerabilities. When Trusted Types are enforced, passing a raw string to __html will fail:

// Before Trusted Types - works but is dangerous
<div dangerouslySetInnerHTML={{ __html: userContent }} />

// After Trusted Types - this throws an error!
<div dangerouslySetInnerHTML={{ __html: userContent }} />

Creating a Trusted Types Policy

Integrate DOMPurify with a Trusted Types policy:

// In your initialization code (before React mounts)
if (window.trustedTypes && trustedTypes.createPolicy && window.DOMPurify) {
 const ttPolicy = trustedTypes.createPolicy('react-sanitize', {
 createHTML: (input) => DOMPurify.sanitize(input, {
 RETURN_TRUSTED_TYPE: true
 })
 });
}

Updating Components for Trusted Types

import { useMemo } from 'react';

function SafeHTMLContent({ content }) {
 const trustedContent = useMemo(() => {
 if (window.trustedTypes?.createPolicy) {
 return window.trustedTypes.createPolicy('react-sanitize').createHTML(content);
 }
 // Fallback for browsers without Trusted Types
 return content;
 }, [content]);

 return (
 <div dangerouslySetInnerHTML={{ __html: trustedContent }} />
 );
}

Handling Third-Party Components

For libraries that use innerHTML internally, create specific policies:

// Policy for rich text editor output
const editorPolicy = trustedTypes.createPolicy('editor-output', {
 createHTML: (input) => DOMPurify.sanitize(input, {
 ALLOWED_TAGS: ['b', 'i', 'em', 'strong', 'p', 'ul', 'li'],
 RETURN_TRUSTED_TYPE: true
 })
});

For React development projects, implementing Trusted Types requires careful coordination between frontend architecture and security requirements to ensure comprehensive protection against DOM-based attacks.

Common Violation Patterns and Solutions

Pattern 1: Direct innerHTML Assignment

Problem: The most common violation pattern.

element.innerHTML = '<div>' + userInput + '</div>'; // Violation!

Solution: Use a policy with sanitization.

const policy = trustedTypes.createPolicy('sanitize', {
 createHTML: (input) => DOMPurify.sanitize(input)
});
element.innerHTML = policy.createHTML('<div>' + userInput + '</div>');

Pattern 2: Template Literals with User Input

Problem: Dynamic HTML construction with template strings.

element.innerHTML = `<div class="${userClass}">${userContent}</div>`;

Solution: Refactor to separate static and dynamic content.

const safeClass = DOMPurify.sanitize(userClass);
element.innerHTML = policy.createHTML(
 `<div class="${safeClass}">${userContent}</div>`
);

Pattern 3: Dynamic Script Loading

Problem: Loading scripts from dynamic URLs.

const script = document.createElement('script');
script.src = analyticsUrl; // Violation if not trusted!

Solution: Validate against an allowlist or use TrustedScriptURL.

const policy = trustedTypes.createPolicy('script-urls', {
 createScriptURL: (input) => {
 const allowed = ['https://analytics.example.com'];
 if (allowed.includes(input)) return input;
 throw new Error('Disallowed script URL');
 }
});

script.src = policy.createScriptURL(analyticsUrl);

Pattern 4: Browser Extensions

Problem: Extensions that modify page DOM directly.

Solution: Extension developers must update their code to use Trusted Types. The integration of Trusted Types often reveals hidden dependencies in third-party code, which is why comprehensive security audits are essential before deployment.

  1. Override CSP header locally to test: Content-Security-Policy: require-trusted-types-for 'script'
  2. Enable automatic breakpoints on Trusted Type violations in DevTools
  3. Use libraries like safevalues or DOMPurify
  4. Test extension workflows to identify violations

By addressing these common patterns systematically, organizations can achieve robust security across their web application infrastructure while maintaining developer productivity and user experience.

Complete Trusted Types Integration Example
1// 1. Policy initialization (run once at app startup)2const ttPolicy = trustedTypes?.createPolicy('default-sanitizer', {3 createHTML: (input) => {4 // Sanitize with DOMPurify5 const sanitized = DOMPurify.sanitize(input, {6 RETURN_TRUSTED_TYPE: true7 });8 return sanitized;9 },10 createScriptURL: (input) => {11 // Validate against allowlist12 const allowedDomains = ['cdn.example.com', 'analytics.google.com'];13 try {14 const url = new URL(input);15 if (allowedDomains.includes(url.hostname)) {16 return input;17 }18 } catch (e) {19 // Invalid URL, return a safe default20 return '';21 }22 throw new Error('Disallowed script URL');23 }24});25 26// 2. Usage in application code27function renderUserContent(htmlContent) {28 const container = document.getElementById('user-content');29 if (ttPolicy) {30 container.innerHTML = ttPolicy.createHTML(htmlContent);31 } else {32 // Fallback for browsers without Trusted Types33 container.innerHTML = DOMPurify.sanitize(htmlContent);34 }35}36 37// 3. Dynamic script loading38function loadAnalytics(url) {39 const script = document.createElement('script');40 script.type = 'text/javascript';41 42 if (ttPolicy) {43 script.src = ttPolicy.createScriptURL(url);44 } else {45 script.src = url; // Fallback46 }47 48 document.head.appendChild(script);49}

Best Practices and Recommendations

Policy Design

  1. Start restrictive, expand as needed: Begin with minimal policies and add more as you discover requirements
  2. Use descriptive names: Policy names should indicate purpose (e.g., 'cms-content', 'user-comments', 'analytics')
  3. Implement least-privilege: Different content sources should have different policies based on trust level
  4. Document policies: Each policy should have clear documentation of its purpose and allowed content

Gradual Rollout Strategy

  1. Start with report-only mode: Use Content-Security-Policy-Report-Only to discover violations without breaking functionality
  2. Monitor violations: Collect reports from report-only mode to identify all locations needing updates
  3. Fix high-priority issues first: Focus on user-generated content areas that pose the highest risk
  4. Enable full enforcement: After thorough testing, switch to enforcement mode
  5. Maintain rollback plan: Have a process to quickly disable enforcement if issues arise

Testing Strategy

  1. Add to CI/CD: Include Trusted Types checks in your continuous integration pipeline
  2. Use DevTools breakpoints: Enable automatic breakpoints on TT violations during development
  3. Integration tests: Create tests that verify TT-compliant code paths work correctly
  4. Real content testing: Test with actual user content to catch edge cases

Performance Considerations

  • DOMPurify caching: Sanitize similar content once and cache the result
  • Server-side sanitization: Consider sanitizing content on the server for static content
  • Minimal policy lookups: Policy creation is cheap, but avoid creating policies in hot paths
  • Monitor sanitization overhead: Use browser performance tools to measure impact

Implementing Trusted Types is part of a comprehensive application security strategy that protects your users and your reputation, while supporting your broader digital marketing initiatives with secure, performant web infrastructure.

Browser Support

2020

Year Chrome/Edge Added Support

83+

Minimum Chrome/Edge Version

3

Trusted Types Available (HTML, Script, ScriptURL)

Major

Browsers Supporting TT (Chrome, Edge, Opera)

Frequently Asked Questions

Secure Your Web Applications with Trusted Types

Implement browser-enforced XSS protection to eliminate entire classes of DOM-based vulnerabilities. Our security experts can help you integrate Trusted Types into your existing codebase.

Sources

  1. MDN Web Docs - Trusted Types API - Primary source for API definitions, injection sink interfaces, and browser compatibility.

  2. Chrome for Developers - Adding Trusted Types to YouTube - Production deployment insights, extension compatibility requirements, and enforcement best practices.

  3. DEV Community - Complete Guide to Trusted Types in React - React integration patterns, DOMPurify integration, and step-by-step implementation examples.

  4. Content Security Policy Reference - require-trusted-types-for - CSP directive syntax, browser support matrix, and error message interpretation.

  5. Google Bug Hunters - Deep Dive into JS Trusted Types Violations - Technical analysis of common Trusted Types violations patterns from Gmail and AppSheet rollouts.