A new security approach using Trusted Types with 'none' policy forces developers to use safe DOM APIs like setHTML(), effectively eliminating DOM-based XSS vulnerabilities.
Perfect Types: The Path to Eliminating DOM-Based XSS
In the ongoing battle against web security vulnerabilities, a new approach called "Perfect Types" is emerging as a potential silver bullet for DOM-based cross-site scripting (XSS) attacks. This method, inspired by Jun Kokatsu's work and refined through practical implementation, leverages Trusted Types in Content Security Policy (CSP) to create an environment where only safe DOM manipulation methods are possible.
The Problem with Traditional DOM XSS Prevention
DOM-based XSS has long been one of the most challenging web security vulnerabilities to address. Unlike reflected or stored XSS, DOM XSS occurs entirely on the client side when untrusted data is used to dynamically construct HTML content. Traditional prevention methods rely on developers manually sanitizing input before using dangerous APIs like innerHTML, document.write(), or insertAdjacentHTML().
The fundamental issue is that these APIs accept plain strings, making it trivial for malicious actors to inject executable code if proper sanitization isn't consistently applied. Even with the best intentions, developers can make mistakes, libraries can have vulnerabilities, or new attack vectors can emerge that bypass existing sanitization logic.
Trusted Types: A Step in the Right Direction
Trusted Types, introduced as part of CSP, represents a significant improvement by creating a type system for HTML content. When enabled with a policy like require-trusted-types-for 'script'; trusted-types 'mypolicy';, it prevents scripts from using dangerous APIs with plain strings. Instead, these APIs require TrustedHTML objects, which can only be created through approved policies.
However, this approach still places the burden on developers to:
- Create and maintain Trusted Type policies
- Ensure all team members use these policies correctly
- Keep policies updated as new attack vectors emerge
- Handle the complexity of policy configuration
Perfect Types: Eliminating the Policy Burden
The Perfect Types approach takes Trusted Types to its logical extreme by using the policy 'none'. The CSP header require-trusted-types-for 'script'; trusted-types 'none'; does two critical things:
- Disables all legacy HTML parsing sinks: APIs like
innerHTML,document.write(), and similar methods that accept plain strings are no longer usable - Prevents any Trusted Type policy creation: No policies can be created, eliminating the need to maintain them
This creates a binary state: either you use the new safe APIs, or your code simply won't work. There's no middle ground, no possibility of misconfiguration, and no policy to maintain.
The Safe APIs: setHTML() and parseHTML()
With Perfect Types enabled, developers must use the new safe APIs:
setHTML()
The Element.setHTML() method provides a direct, safe way to insert HTML content into the DOM. Unlike innerHTML, it's designed with security in mind and works seamlessly with Trusted Types.
Document.parseHTML()
For more complex scenarios, Document.parseHTML() allows parsing HTML into a temporary document. The key security consideration here is to avoid extracting HTML strings from this temporary document using methods like innerHTML, as this can lead to mutation XSS (mXSS) vulnerabilities even with properly sanitized content.
Instead, developers should:
- Parse the HTML into a temporary document
- Use DOM APIs like
querySelectorto select the desired elements - Use
cloneNodeor other Node APIs to move elements to the target document
The Security Guarantee
The beauty of Perfect Types lies in its simplicity and completeness. By making it impossible to use dangerous APIs and eliminating the need for policy management, it provides a strong guarantee:
If your code works under Perfect Types, it cannot contain DOM-based XSS vulnerabilities.
This shifts the security model from "try to prevent" to "it's impossible to make this mistake." It's a fundamental change in how we approach web security.
Practical Considerations
Implementing Perfect Types requires some adjustment:
- Migration effort: Existing code using
innerHTMLor similar APIs will need to be updated - Library compatibility: Third-party libraries must support the new APIs
- Development workflow: Developers need to understand and consistently use the safe APIs
However, the long-term benefits include reduced security review overhead, fewer vulnerabilities, and a more robust security posture.
The Future of Web Security
Perfect Types represents an evolution in web security thinking. Rather than trying to make dangerous things safe through policies and sanitization, it makes dangerous things impossible. This aligns with modern security principles that favor removing attack surface over trying to defend it.
As more browsers adopt and implement these APIs, and as the web development community embraces this approach, we may see a future where DOM-based XSS becomes a relic of the past—not because we're better at preventing it, but because it's simply no longer possible.
The original article on web.dev provides additional context and examples for implementing Perfect Types in real-world applications.
Comments
Please log in or register to join the discussion