Cross-Site Scripting (XSS) originating in the DOM remains one of the most pervasive vulnerabilities in modern client-side single-page applications. While classic Content Security Policy (CSP) mitigates reflected and stored XSS by restricting script origins and blocking inline scripts, it historically struggled to prevent vulnerable DOM sinks (such as Element.innerHTML, document.write, or location.href) from executing unsanitised strings passed via client-side routing. The W3C Trusted Types specification resolves this systemic flaw. By configuring require-trusted-types-for 'script' in your CSP header, Google Chrome disables raw string injection into all dangerous DOM execution sinks.
Understanding Trusted Types and DOM Execution Sinks
Trusted Types transforms browser DOM APIs from accepting arbitrary string primitives into accepting only cryptographically typed policy objects:
require-trusted-types-for 'script': Instructs Google Chrome to throw a runtimeTypeErrorwhenever an application passes a raw string to an injection sink (such asinnerHTML,outerHTML,script.src, orWorker()).- Trusted Type Objects: The browser recognizes three distinct wrapper types:
TrustedHTML,TrustedScript, andTrustedScriptURL. trustedTypes.createPolicy(): The only sanctioned mechanism to produce trusted objects. Enforces centralized sanitisation logic (using DOMPurify or custom token sanitisation) before any HTML enters the DOM tree.trusted-types [policyNames]: Restricts which policy names are authorized to create trusted objects, preventing rogue third-party dependencies from creating bypass policies.
Configuring Server Headers in Nginx and Express
Deploy the strict Trusted Types directive as part of your application’s Content-Security-Policy HTTP response header:
# Nginx Production Configuration
server {
listen 443 ssl http2;
server_name portal.example.com;
# Enforce Trusted Types for all script injection sinks
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'nonce-rAnd0m123'; require-trusted-types-for 'script'; trusted-types dompurify-policy default;" always;
# Defense-in-depth headers
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "DENY" always;
location / {
proxy_pass http://frontend_upstream;
proxy_set_header Host $host;
}
}
Authoring the Trusted Types Sanitisation Policy in JavaScript
Implement a centralized DOMPurify sanitisation policy in your application bootstrap routine:
// 1. Verify browser support for Trusted Types
if (window.trustedTypes && trustedTypes.createPolicy) {
// 2. Create authorized sanitisation policy
const sanitizerPolicy = trustedTypes.createPolicy('dompurify-policy', {
createHTML: (dirtyInput) => {
// Strip dangerous tags, event handlers, and script payloads
return DOMPurify.sanitize(dirtyInput, { RETURN_TRUSTED_TYPE: false });
},
createScriptURL: (url) => {
// Validate allowed CDN hosts
const parsed = new URL(url, window.location.origin);
if (parsed.origin === window.location.origin || parsed.origin === 'https://cdn.example.com') {
return url;
}
throw new TypeError("Blocked untrusted script URL: " + url);
}
});
// 3. Inject safe HTML into the DOM sink using the policy
function renderUserComment(element, rawUserText) {
// Correct: Passes a TrustedHTML object produced by the policy
element.innerHTML = sanitizerPolicy.createHTML(rawUserText);
}
} else {
console.warn("Trusted Types not natively supported. Relying on fallback sanitisation.");
}
Debugging Trusted Types Violations in Chrome DevTools
Google Chrome provides specialized developer tools to trace and pinpoint Trusted Types failures:
- Open Google Chrome, navigate to your target web application, and open DevTools via F12.
- Select the Console tab. If any library attempts to assign a raw string to
innerHTML, Chrome displays an explicit security error:Uncaught TypeError: Failed to set the 'innerHTML' property on 'Element': This document requires 'TrustedHTML' assignment. - Click the stack trace link adjacent to the error to highlight the exact JavaScript line performing the unsanitised string assignment.
- Open the Sources tab, expand the Breakpoints section in the right sidebar, and tick Trusted Type Violations. Chrome halts execution directly inside the call stack before the DOM sink executes, allowing deep inspection of variables.
- To test deployment safety without breaking existing production features, configure your CSP in report-only mode:
Content-Security-Policy-Report-Only: require-trusted-types-for 'script'; report-uri /api/csp-violations;