Last week at AngularConnect in London, I heard for the first time—thanks to a talk by Martina Krauss—about a powerful new feature that reshapes how we can defend against cross-site scripting. I found it so interesting that I want to give you a brief introduction to the feature. Cross-site scripting (XSS) attacks (what is XSS?) have been one of the most common attack vectors in web apps for years. With the Trusted Types feature, a huge class of DOM-based XSS issues becomes much harder to exploit—great news for all of us.
What are Trusted Types?
JavaScript offers lots of ways to create or inject HTML and script from strings—think innerHTML
, insertAdjacentHTML
, document.write
, script.src
, iframe.srcdoc
, and so on. These are “injection sinks.” They’re essential, but they’re risky: they’ll accept whatever you pass to them. Attackers abuse this to inject malicious code.
If you’re assigning HTML strings to these sinks, you should sanitize them first. What is sanitizing? But people forget, code paths are tricky, and third‑party libraries don’t always sanitize correctly.
Enter Trusted Types. With Trusted Types enabled, the browser refuses plain strings in dangerous sinks. Instead, those sinks require special values—TrustedHTML, TrustedScript, or TrustedScriptURL—that can only be created by policies you define. Your policy can sanitize and validate input first, then hand the browser a “trusted” value. That closes off a big DOM XSS attack surface.
Important: Trusted Types don’t magically sanitize for you. You’re responsible for writing (or using) a safe policy that sanitizes/validates input.
How do Trusted Types work?
Trusted Types have two parts working together: a Content Security Policy (CSP) directive and a trustedTypes
global you use to create policies.
CSP
You enforce and configure Trusted Types with CSP. For example:
- Header form:
Content-Security-Policy: trusted-types app dompurify; require-trusted-types-for 'script';
- Meta tag form:
<meta http-equiv="Content-Security-Policy" content="trusted-types app dompurify; require-trusted-types-for 'script'">
This has two directives:
trusted-types app dompurify
:
Lists the policy names your page is allowed to create (e.g., “app”, “dompurify”). You can list one or many. You can also use 'none' to block policy creation or '*' to allow any (not recommended).require-trusted-types-for 'script'
:
Enforces Trusted Types for script-creating and HTML-parsing sinks (e.g.,innerHTML
,insertAdjacentHTML
,iframe.srcdoc
,document.write
and URLs forscript.src
/Workers). The only token currently is 'script'.
Tip: You can start with Report-Only to measure impact before enforcing:
Content-Security-Policy-Report-Only: trusted-types app dompurify; require-trusted-types-for 'script';
trustedTypes global
The second piece is the global trustedTypes
object on window
. You create “policies” with it—named objects whose methods you implement to sanitize and return trusted values. The browser will then accept those values in dangerous sinks.
Example:
// Example: using DOMPurify inside a policy to sanitize
const policy = window.trustedTypes.createPolicy('app', {
createHTML: (input) => DOMPurify.sanitize(input),
createScript: (input) => input, // This could sanitize for script usage
createScriptURL: (input) => input, // This could sanitize for scriptURL
});
const untrustedHTML = '<h1>Hello World</h1>';
document.getElementById('container').innerHTML = policy.createHTML(untrustedHTML);
Notes:
- You can define a special policy named 'default'. In enforcement mode, if code accidentally passes a string to a sink, the browser will run it through your 'default' policy.
How can I start using Trusted Types today?
Trusted Types are supported in Chromium-based browsers like Chrome and Edge. In Firefox, implementation work is ongoing and it isn’t enabled by default yet. Safari/WebKit is also in progress.
If you want to adopt the pattern ahead of full native coverage, you can use the WICG/W3C Trusted Types polyfill. It won’t enforce protections to the full extent of native support, but it’s very useful for surfacing violations and preparing your app for enforcement.
Summary
The new Trusted Types feature greatly helps to protect us against XSS attacks. It consists of two parts: enforcement rules and policies, which help ensure that dynamic content added to the DOM is trustworthy. It is currently only supported natively in Chromium-based browsers, with development in Firefox and Safari ongoing. However, polyfills are available, so Trusted Types can be adopted today.
Small demo
<!doctype html>
<html lang="en">
<head>
<meta http-equiv="content-security-policy" content="trusted-types foo; require-trusted-types-for 'script';">
</head>
<body>
<div id="container"></div>
<script>
const myPolicy = window.trustedTypes.createPolicy("foo", {
createHTML: (input) => input,
createScript: (input) => input,
createScriptURL: (input) => input,
});
const untrustedHTML = "<h1>Hello World</h1>";
// Works
document.getElementById("container").innerHTML = myPolicy.createHTML(untrustedHTML);
try {
// Throws because untrustedHTML is a string, not TrustedHTML
document.getElementById("container").innerHTML = untrustedHTML;
}catch (e){
console.error(e);
}
// Throws because bar was not allowed in the CSP
const myPolicy2 = window.trustedTypes.createPolicy("bar", {
createHTML: (input) => input,
createScript: (input) => input,
createScriptURL: (input) => input,
});
</script>
</body>
</html>
Sources
https://github.com/w3c/trusted-types https://developer.mozilla.org/en-US/docs/Web/API/Trusted_Types_API
https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Content-Security-Policy/trusted-types
https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/meta/http-equiv
https://github.com/cure53/DOMPurify
Top comments (0)