18 years have passed since Cross-Site Scripting (XSS) has been identified as a web vulnerability class. Since then, numerous efforts have been proposed to detect, fix or mitigate it. We've seen vulnerability scanners, fuzzers, static & dynamic code analyzers, taint tracking engines, linters, and finally XSS filters, WAFs and all various flavours of Content Security Policy.
Various libraries have been created to minimize or eliminate the risk of XSS: HTML sanitizers, templating libraries, sandboxing solutions - and yet XSS is still one of the most prevalent vulnerabilities plaguing web applications.
It seems like, while we have a pretty good grasp on how to address stored & reflected XSS, "solving" DOM XSS remains an open question. DOM XSS is caused by ever-growing complexity of client-side JavaScript code (see script gadgets), but most importantly - the lack of security in DOM API design.
But perhaps we have a chance this time? Trusted Types is a new browser API that
allows a web application to limit its interaction with the DOM, with the goal of obliterating
DOM XSS. Based on the battle-tested design that prevents XSS in most of the Google web applications, Trusted Types add the DOM XSS prevention API to the browsers. Trusted Types allow to isolate the application components that may potentially introduce DOM XSS into tiny, reviewable pieces, and guarantee that the rest of the code is DOM-XSS free. They can also leverage existing solutions like autoescaping templating libraries, or client-side sanitizers to use them as building blocks of a secure application.
Trusted Types have a working polyfill, an implementation in Chrome and integrate well with existing JS frameworks and libraries. Oddly similar to both XSS filters and CSP, they are also fundamentally different, and in our opinion have a reasonable chance of eliminating DOM XSS - once and for all.
3. Google Vulnerability Reward Program
payouts in 2018
XSS 35.6%
Non-web issues 49.1%
Mobile app vulnerabilities
Business logic (authorization)
Server /network misconfigurations
...
4. Source: HackerOne report, 2018
Consumer
Goods
Financial services &
insurance Government Healthcare Media &
Entertainment
Professional
services
Retail &
Ecommerce
Technology Telecom Transportation Travel &
Hospitality
Figure 5: Listed are the top 15 vulnerability types platform wide, and the percentage of vulnerabilities received per industry
Cross Site scripting (XSS)
Information disclosure
Improper authentication
Violation of secure
design principles
Cross-site request
forgery (CSRF)
Open redirect
Privilege Escalation
Improper access control
Cryptographic issues
Denial of service
Business logic errors
Code injection
SQL injection
Vulnerabilities by industry
5. Paid bounties by vulnerability
on Mozilla websites in 2016 and 2017
Source: @jvehent, Mozilla
CountofVulnerability
w
sec-xss
w
sec-applogic
w
sec-disclosure
w
sec-im
personation
w
sec-objref
w
sec-injection
w
sec-appm
isconfig
w
sec-authentication
w
sec-redirect
w
sec-oscm
d
w
sec-http-header-inject
w
sec-serverm
isconfig
w
sec-sqli
w
sec-authorization
w
sec-crossdom
ain
w
sec-csrf
6. DOM XSS is a client-side XSS variant caused by the DOM API not being secure by
default.
● User controlled strings get converted into code
● via dangerous and error-prone DOM sinks like:
innerHTML, window.open(), ~60 other DOM sinks
var foo = location.hash.slice(1);
document.querySelector('#foo').innerHTML = foo;
How does DOM XSS happen?
Example: https://example.com/#<img src=x onerror=alert('xss')>
8. Growing problem
● DOM sinks can be used by your own code
○ … or the libraries you use
○ … or the scripts you load (analytics?)
○ … or the script that they load at runtime.
● Each of those potentially introduces DOM XSS
● Static analysis for JS does not work reliably
● At Google, DOM XSS is already the most common XSS variant.
9. We know how to address it!
Safe Types (link)
● 6+ years of experience
● Protects Gmail and almost all other
Google applications
● Evidence of efficacy
● Securely produce values that end up in DOM
● Implemented as Java{Script},Go, … libraries
● We're porting this approach directly to the browsers
11. Strongly typed DOM API.
● Don't pass (HTML, URL, script URL) strings to the DOM sinks
● Use objects instead
● DOM already supports it:
● Web platform (or polyfill) provides typed objects:
TrustedHTML, TrustedScript, TrustedURL, TrustedScriptURL
● Security rules & controls are based on types
The idea behind Trusted Types
el.innerHTML = {toString: () => 'hello'};
el.innerHTML // 'hello'
12. When Trusted Types are not enforced:
DOM sinks accepts strings as usual
DOM sinks accept typed objects
element.innerHTML = aTrustedHTML;
The idea behind Trusted Types
13. When Trusted Types are enforced:
DOM sinks reject strings
DOM sinks accept typed objects
Content-Security-Policy: trusted-types myPolicy
element.innerHTML = location.hash.slice(1); // a string
element.innerHTML = aTrustedHTML; // created via a TrustedTypes policy
The idea behind Trusted Types
14. When Trusted Types are in reporting mode:
DOM sinks accept & report strings
DOM sinks accept typed objects
Content-Security-Policy-Report-Only: trusted-types myPolicy; report-uri /report
element.innerHTML = location.hash.slice(1); // a string
element.innerHTML = aTrustedHTML; // created via a TrustedTypes policy
The idea behind Trusted Types
15. 1. Create policies with validation rules
2. Use the policies to create Trusted Type objects
3. Enforce "myPolicy" by setting a Content Security Policy header
Creating Trusted Types
Content-Security-Policy: trusted-types myPolicy myOtherPolicy
const SanitizingPolicy = TrustedTypes.createPolicy('myPolicy', {
createHTML(s: string) => myCustomSanitizer(s)
}, false);
// Calls myCustomSanitizer(foo).
const trustedHTML = SanitizingPolicy.createHTML(foo);
element.innerHTML = trustedHTML;
16. Control policy creation:
● Policy name whitelist:
● No duplicate policy names
Control policy usage:
● Policies are JavaScript objects
● Lock them in a module, inside a local function variable etc.
Control over policies
Content-Security-Policy: trusted-types myPolicy myOtherPolicy
17. The "default" policy is called as a fallback when a string is assigned to a sink.
Good way to get started and to identify risky DOM assignments.
Trusted Types - default policy
Content-Security-Policy: trusted-types myPolicy default
TrustedTypes.createPolicy('default', {
createHTML(s) {
console.log("Please fix! Insecure string assignment:", s);
return s;
}
}, true);
19. →
Benefits
Source ... Policy Trusted Type→ → → ... DOM sink→
● Reduced attack surface - The risky data flow will always be:
● Isolates security-sensitive code from the rest of the applications
● No DOM XSS if policies are secure and access restricted
● Compile time & runtime security validation
20. Trusted Types in practice
● Policies in a few trusted components
○ Frameworks
○ Templating engines
○ HTML sanitizers (DOMPurify)
● It's easy to add support to modern frameworks (sample patch for Angular)
● Code size overhead negligible
○ 66 bytes of the smallest polyfill
○ ~300 bytes in Google Closure
Porting modern applications is easy (we're already doing this for our apps!)
21. ● Active development, looking for your feedback!
● Support in Chromium browsers (origin trial - tinyurl.com/try-trusted-types)
● Discussion group - trusted-types@googlegroups.com
● W3C specification draft - wicg.github.io/trusted-types/
● Polyfills & documentation at bit.ly/trusted-types
Working on external integrations:
● DOMPurify
● Modern JS frameworks
● TypeScript type definitions - definitelytyped.org
● Secure policies library & other tooling (coming soon!)
Project status