Prevent DOM-based XSS with Trusted Types and Content Security Policy with report-uri
With Trusted Types enabled, when a plain string is passed to a so-called sink like the innerHTML or the document.write() method without it being escaped by the Trusted Types policy, a report is generated by the browser.
Because the policy is enforced, the string will not actually be passed to the sink, protecting your app against DOM-based cross-site scripting (XSS) attacks.
The CSP response header:
Content-Security-Policy: require-trusted-types-for 'script'; report-uri https://chou.has.report/report
-
require-trusted-types-for 'script': enable Trusted Types for DOM XSS sinks ('script'is the only available value) -
report-uri: where to send violation reports- Reporting would also work with the
report-todirective, see the CSP demo, but let's keep things simple here
- Reporting would also work with the
Trusted Types with a custom policy
#html sink: … / #xss sink: …
<script>
const escapePolicy = trustedTypes.createPolicy('escapePolicy', {
createHTML: string => string.replaceAll('<', '<'),
});
document.getElementById('prompt-html').onclick = function() {
const html = prompt('Enter any HTML', 'foo <strong>bar</strong>');
if (html) {
document.getElementById('html').innerHTML = escapePolicy.createHTML(html);
document.getElementById('xss').innerHTML = html;
}
}
</script>
The #html sink:
- Is allowed in a way that it will display the HTML you've entered, escaped
-
Uses the
createHTML()method of theescapePolicycreated bytrustedTypes.createPolicy()- … to replace all
<("less than") characters with<entity, a very simple and naive escaping function
- … to replace all
-
If you enter let's say
<em>createHTML()will convert it to<em>- Wrap it in a
TrustedHTMLobject - And pass it to the
innerHTMLsink
- Note that your browser, not the policy, will convert
>to>as well, so in Developer Tools you will see<em>
#xss sink:
- Is blocked, the HTML you've entered will not be passed to the
innerHTMLproperty - Because it doesn't use the
createHTML()method of theescapePolicy - But it tries to pass a plain string which is forbidden with Trusted Types enabled
- Will trigger a report, check Developer tools (Network and Console tabs)
- Check your reports
Related specs & documents
- My article about DOM XSS and Trusted Types (also available in Czech)
- Trusted Types Editor's Draft
- Prevent DOM-based cross-site scripting vulnerabilities with Trusted Types on web.dev
- Trusted Types API on MDN