EXERCISE 3 — How Trusted Types prevents DOM-based XSS ====================================================== THE OLD MODEL: "remember to sanitize before innerHTML." - DOM XSS happens when untrusted data reaches a dangerous sink (innerHTML, outerHTML, document.write, insertAdjacentHTML, script.src, eval, ...). - The conventional defence is a DISCIPLINE: every developer must remember to sanitize (DOMPurify) the data before every such assignment. - This is FRAGILE: it fails the instant ONE sink, in one file, by one developer, is missed -- and large codebases have many sinks. You can't easily prove every path is covered; new code can reintroduce a raw sink at any time. The default is UNSAFE and safety depends on vigilance. THE TRUSTED TYPES MODEL: "the sink rejects raw strings." - Enabled via CSP: Content-Security-Policy: require-trusted-types-for 'script' - With it on, the dangerous sinks STOP ACCEPTING PLAIN STRINGS. Assigning a raw string to innerHTML (etc.) THROWS a TypeError -- the browser refuses. - The ONLY way to use the sink is to pass a special TYPED object (TrustedHTML / TrustedScript / TrustedScriptURL) that can only be produced by a Trusted Types POLICY you define: const policy = trustedTypes.createPolicy("safe", { createHTML: s => DOMPurify.sanitize(s) }); el.innerHTML = policy.createHTML(userString); // allowed & sanitized - So every value reaching a sink has, by construction, passed through your policy's createHTML -- which sanitizes. Unsanitized strings can't reach the sink at all. WHY THIS CONVERTS A FRAGILE DISCIPLINE INTO AN ENFORCED GUARANTEE: - It INVERTS THE DEFAULT. Before: the sink accepts anything, and safety is an opt-in habit you must apply everywhere (easy to forget). After: the sink REJECTS everything unsafe by default, and the only path through is the sanitizing policy. Forgetting doesn't silently create a hole -- it produces a loud runtime error during development/testing. - The browser ENFORCES it, not the developer. A missed sanitize call is no longer a quiet vulnerability; it's an exception that surfaces the mistake. "Don't forget to sanitize" (erodes over time) becomes "you physically cannot pass an unsanitized string to a sink" (holds by construction). WHAT YOU AUDIT INSTEAD: - The single CHOKEPOINT: the Trusted Types POLICY (or the small set of policies). Instead of auditing every innerHTML/document.write/eval across the whole codebase, you review the createHTML/createScript implementations in your policy to confirm they sanitize correctly (e.g. delegate to DOMPurify), and you keep the policy list minimal/locked down (CSP can restrict which policy names may be created). - One place to get right, browser-enforced everywhere else. That's why Trusted Types is the strongest STRUCTURAL defence against DOM XSS. NOTE: Trusted Types addresses DOM (client-side sink) XSS specifically. You still need output encoding/sanitization for server-rendered reflected/stored XSS, and CSP as the broader backstop. It complements -- doesn't replace -- the earlier defences.