How Trusted Types API Prevents DOM XSS
DOM-based cross-site scripting (DOM XSS) remains one of the most prevalent and difficult-to-detect web vulnerabilities. The Trusted Types API provides a robust, browser-enforced defense mechanism that systematically mitigates DOM XSS by locking down dangerous DOM execution sinks. By requiring input passed to these sinks to be explicitly vetted and wrapped in typed objects rather than plain strings, Trusted Types shifts security enforcement from manual, error-prone code reviews directly to the browser runtime.
The Mechanism of DOM XSS and Dangerous Sinks
DOM XSS occurs when untrusted user data flows into an execution sinkāan API endpoint in the browser that can execute arbitrary code. Common injection sinks include properties and methods such as:
Element.innerHTMLandElement.outerHTMLdocument.write()location.hrefscript.srceval()andsetTimeout(string)
Historically, web applications passed raw strings to these sinks. If an application failed to sanitize or validate input anywhere in a complex call stack, an attacker could inject malicious scripts that execute in the context of the user's session.
Restricting Sinks with Typed Objects
The Trusted Types API fundamentally alters how dangerous sinks
behave. When enabled, the browser rejects plain string arguments passed
to sensitive DOM APIs, throwing a runtime TypeError.
Instead, these sinks will only accept one of three specialized,
immutable objects:
TrustedHTML: Required for sinks that parse and render HTML markup, such asinnerHTML.TrustedScript: Required for sinks that execute JavaScript directly, such as inline script contents oreval().TrustedScriptURL: Required for sinks that load external executable resources, such as creating a<script src="...">element.
Because developers cannot directly instantiate these objects using standard constructors, malicious payloads passed as raw strings are neutralized before they can be evaluated.
Centralized Sanitization Through Policies
Trusted Types objects can only be generated through explicitly
defined policies. Using the
window.trustedTypes.createPolicy() method, developers
create a centralized ruleset that takes an untrusted string, applies
sanitization or validation logic, and returns a verified Trusted Type
instance.
For example, a policy using a sanitizer library ensures that any HTML passed to it is stripped of active code:
const sanitizePolicy = trustedTypes.createPolicy('sanitizer-policy', {
createHTML: (untrustedInput) => DOMPurify.sanitize(untrustedInput)
});
// Throws a TypeError:
element.innerHTML = untrustedInput;
// Successfully executes:
const safeHTML = sanitizePolicy.createHTML(untrustedInput);
element.innerHTML = safeHTML;This architecture centralizes security logic. Instead of auditing thousands of instances where raw strings are assigned to DOM sinks across an entire codebase, security teams only need to review the defined Trusted Types policies.
Enforcement via Content Security Policy
Trusted Types is deployed and enforced using the
Content-Security-Policy (CSP) HTTP response header:
- Enforcement mode:
Content-Security-Policy: require-trusted-types-for 'script';locks down the sinks immediately, causing the browser to block any raw string assignments. - Allowlisting policies:
Content-Security-Policy: require-trusted-types-for 'script'; trusted-types sanitizer-policy;specifies exactly which policies are allowed to create trusted objects, preventing unauthorized script execution via rogue policy creation. - Testing and migration: Using the
Content-Security-Policy-Report-Onlyheader allows developers to monitor violations and identify sinks across legacy applications without breaking existing functionality.
By removing the reliance on developers remembering to sanitize data at every individual sink, the Trusted Types API provides structural immunity against DOM-based XSS vulnerabilities.