javascripthtmlsecuritycontent-security-policy

This document requires 'TrustedScriptURL' assignment


After adding require-trusted-types-for 'script'; in my Content-Security-Policy header, which introduced from Chrome 83 Beta to help lock down DOM XSS injection sinks,

when I open my website, it becomes a blank page. I got many these three kinds of errors in my console. (Chrome version 83.0.4103.61)

This document requires 'TrustedScript' assignment.

This document requires 'TrustedScriptURL' assignment.

TypeError: Failed to set the 'src' property on 'HTMLScriptElement': This document requires 'TrustedScriptURL' assignment.

I have read the article Prevent DOM-based cross-site scripting vulnerabilities with Trusted Types. However, the article only says how to handle TrustedHTML, but not TrustedScript or TrustedScriptURL.

Any guide will be helpful. Thanks!


Solution

  • We have been running into the very same problem.

    Here's how you fix it:

    1. Install the DOMPurify library. npm install --save DOMPurify

    2. Create a file trusted-security-policies.js.

    3. In the entry point for your bundler (like e.g. webpack), import this file first (before any code that potentially violates the content security policy):

      import './path/to/trusted-security-policies';
      
    import DOMPurify from 'dompurify';
    
    if (window.trustedTypes && window.trustedTypes.createPolicy) { // Feature testing
        window.trustedTypes.createPolicy('default', {
            createHTML: (string) => DOMPurify.sanitize(string, {RETURN_TRUSTED_TYPE: true}),
            createScriptURL: string => string, // warning: this is unsafe!
            createScript: string => string, // warning: this is unsafe!
        });
    }
    

    What this does: Whenever a string is assigned to be parsed as HTML, or as a URL, or as a script, the browser automatically passes this string through the defined handler function.

    For HTML, the HTML is being sanitized from potential XSS code by the DOMPurify library.

    For scriptURL and script, the string is just passed through. Please note that this effectively disables security for these two parts and should only be used for as long as you haven't identified how to make these strings safe yourself. As soon as you have that, replace the handler functions accordingly.


    Edit, December 2021: I was able to contribute to DOMPurify so the library now also can be configured to work if you have the need to use custom elements in your HTML strings, as well as custom attributes (which prior to release 2.3.4 were simply removed in the sanitization process):

    /**
     * Control behavior relating to Custom Elements
     */
     
    // DOMPurify allows to define rules for Custom Elements. When using the CUSTOM_ELEMENT_HANDLING 
    // literal, it is possible to define exactly what elements you wish to allow (by default, none are allowed).
    //
    // The same goes for their attributes. By default, the built-in or configured allow.list is used.
    //
    // You can use a RegExp literal to specify what is allowed or a predicate, examples for both can be seen below.
    // The default values are very restrictive to prevent accidental XSS bypasses. Handle with great care!
    
    
    var clean = DOMPurify.sanitize(
        '<foo-bar baz="foobar" forbidden="true"></foo-bar><div is="foo-baz"></div>',
        {
            CUSTOM_ELEMENT_HANDLING: {
                tagNameCheck: null, // no custom elements are allowed
                attributeNameCheck: null, // default / standard attribute allow-list is used
                allowCustomizedBuiltInElements: false, // no customized built-ins allowed
            },
        }
    ); // <div is=""></div>
     
    var clean = DOMPurify.sanitize(
        '<foo-bar baz="foobar" forbidden="true"></foo-bar><div is="foo-baz"></div>',
        {
            CUSTOM_ELEMENT_HANDLING: {
                tagNameCheck: /^foo-/, // allow all tags starting with "foo-"
                attributeNameCheck: /baz/, // allow all attributes containing "baz"
                allowCustomizedBuiltInElements: false, // customized built-ins are allowed
            },
        }
    ); // <foo-bar baz="foobar"></foo-bar><div is=""></div>
      
    var clean = DOMPurify.sanitize(
        '<foo-bar baz="foobar" forbidden="true"></foo-bar><div is="foo-baz"></div>',
        {
            CUSTOM_ELEMENT_HANDLING: {
                tagNameCheck: (tagName) => tagName.match(/^foo-/), // allow all tags starting with "foo-"
                attributeNameCheck: (attr) => attr.match(/baz/), // allow all containing "baz"
                allowCustomizedBuiltInElements: true, // allow customized built-ins
            },
        }
    ); // <foo-bar baz="foobar"></foo-bar><div is="foo-baz"></div>