Repository: mozilla/django-csp

Issue:
Adding Support for Trusted Types
### Background
Trusted Types is a relatively new web application security feature that protects against DOM XSS. Trusted Types enforcement is currently supported in Chrome 83 and Android Webview. Even where Trusted Types are not enforced by the browser they can still be used to reduce the DOM XSS attack surface.

**_DOM XSS_**
Cross site scripting (XSS) is one of the most prevalent web security vulnerabilities. DOM XSS occurs on the client side when Javascript calls dangerous functions with user supplied input. Examples of dangerous sink functions include innerHTML, outerHTML, eval, and setTimer. Nonce-based CSP already helps prevent server-side XSS. Trusted types help prevent DOM-based or client-side XSS caused by these sink functions. 

**_How It Works_**
Trusted Types are enabled using CSP directives. When enabled this way, the browser will require user supplied data to be processed before being sent to a sink function. Sink functions can only be called with special trusted types objects that tell the browser the data has been sanitized and should be trusted. There are three types of trusted objects; TrustedHTML, TrustedScript, and TrustedScriptURL which are used and created depending on what object type a sink expects.

Trusted objects are created by calling policies. Policies are functions that sanitize input, enforce security rules, and return a trusted type object. Trusted objects can also be created by some libraries like DOMPurify.

If enabled with CSP’s report only mode, browsers will report Trusted Types violations but not block them.

For more information on Trusted Types visit [here](https://web.dev/trusted-types/) and [here](https://w3c.github.io/webappsec-trusted-types/dist/spec/#integration-with-content-security-policy).


### Proposed Changes to Django-CSP
**_Add require-trusted-types-for Directive_**
To enable Trusted Types with CSP two new directives are required. The require-trusted-types-for directive should be configurable using Django-CSP. This directive enables Trusted Types by specifying for which groups of injection sinks trusted types should be expected. Only one value currently exists for this directive, ‘script’, which requires Trusted Types for DOM XSS injection sinks.

**_Add trusted-types Directive_**
The trusted-types directive should be configurable using Django-CSP. This directive can be used to control the creation of Trusted Types policies. Only policies with names present in this directive can be created. It also allows for the creation/declaration of a default policy. When this directive is not present, policies cannot be created; so if trusted types are expected, injection sinks can not be used at all. 

**_Additional Documentation_**
These two new CSP directives should be added to the Configuring Django-CSP documentation. A new page should be added to the documentation providing more information on Trusted Types and steps for how to build a compliant web application.

Discussion:

Patch:
diff --git a/csp/utils.py b/csp/utils.py
index d960f30..a0d5c31 100644
--- a/csp/utils.py
+++ b/csp/utils.py
@@ -45,6 +45,11 @@ def from_settings():
         'report-uri': getattr(settings, 'CSP_REPORT_URI', None),
         'report-to': getattr(settings, 'CSP_REPORT_TO', None),
         'require-sri-for': getattr(settings, 'CSP_REQUIRE_SRI_FOR', None),
+        #trusted Types Directives
+        'require-trusted-types-for': getattr(
+            settings,
+            'CSP_REQUIRE_TRUSTED_TYPES_FOR', None),
+        'trusted-types': getattr(settings, 'CSP_TRUSTED_TYPES', None),
         # Other Directives
         'upgrade-insecure-requests': getattr(
             settings, 'CSP_UPGRADE_INSECURE_REQUESTS', False),
diff --git a/docs/configuration.rst b/docs/configuration.rst
index 8056f6d..7f1941e 100644
--- a/docs/configuration.rst
+++ b/docs/configuration.rst
@@ -95,6 +95,14 @@ These settings affect the policy in the header. The defaults are in
 ``CSP_UPGRADE_INSECURE_REQUESTS``
     Include ``upgrade-insecure-requests`` directive. A boolean. *False*
     See: upgrade-insecure-requests_
+``CSP_REQUIRE_TRUSTED_TYPES_FOR``
+    Include ``reuire-trusted-types-for`` directive. A tuple or list. *None*
+    Valid values: ``'script'``
+    Note: This doesn't use default-src as a fall-back.
+``CSP_TRUSTED_TYPES``
+    Include ``trusted-types`` directive. A tuple of list. *This header is empty by default*
+    Valid values: a list of allowed policy names that may include ``default`` and/or ``'allow-duplicates'``
+    Note: This doesn't use default-src as a fall-back.
 ``CSP_BLOCK_ALL_MIXED_CONTENT``
     Include ``block-all-mixed-content`` directive. A boolean. *False*
     See: block-all-mixed-content_
diff --git a/docs/index.rst b/docs/index.rst
index 5f8cad4..5597cfb 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -23,6 +23,7 @@ Contents:
    configuration
    decorators
    nonce
+   trusted_types
    reports
    contributing
 
diff --git a/docs/trusted_types.rst b/docs/trusted_types.rst
new file mode 100644
index 0000000..638536c
--- /dev/null
+++ b/docs/trusted_types.rst
@@ -0,0 +1,137 @@
+===================================
+Implementing Trusted Types with CSP
+===================================
+
+``DOM Cross-site Scripting``
+============================
+Cross-site scripting (XSS) is one of the most prevalent vulnerabilities
+on the web. Nonce-based CSP is used to prevent server-side XSS. Trusted
+Types are used to prevent client-side or DOM-XSS_. Trusted Types rely on
+the browser to enforce the policy that is provided to it. Currently, Trusted
+Types are supported on Chrome 83 and Android Webview. Many browsers are in the
+process of adding support. Check back for updated compatibility_.
+
+Follow the simple steps below to make your web application Trusted Types
+compliant.
+
+
+``Step 1: Enable Trusted Types and Report Only Mode``
+=====================================================
+Trusted Types require data to be processed before being sent to a risky "sink" where DOM XSS might occur, such as when assigning to Element.innerHTML or calling document.write. When enforced, Trusted Types will tell the
+browser to block any data that is not properly processed. In order to avoid
+this, you must fix offending parts of your code. To see where adjustments will
+be required, turn on trusted types and report only mode.
+
+Configure django-csp so that ``CSP_REQUIRE_TRUSTED_TYPES_FOR`` is set to *‘script’*.
+
+Configure django-csp so that ``CSP_REPORT_ONLY`` is set to *True*.
+
+Configure django-csp so that ``CSP_REPORT_URI`` is set to an app or CSP report processing service that you control.
+
+Now trusted types violations will be reported to your ``CSP_REPORT_URI`` without blocking any of your application’s functionalities.
+
+
+``Step 2: Fixing Trusted Types Violations``
+===========================================
+There are four ways to resolve trusted types violations. They are explained
+here in order of preference.
+
+Rewrite the Code
+----------------
+It may be possible for your code to be rewritten without using dangerous
+functions. For example, instead of dynamically placing an image using the
+dangerous ``innerHTML`` sink, the image could be created with
+``document.createElement`` and placed using the ``appendChild`` function.
+
+Rewriting may be possible for any of the dangerous sinks, which are listed here.
+
+* Script manipulation:
+    * ``<script src>`` and setting text content of ``<script>`` elements.
+        * Tip: Avoid creating scripts at run time
+        * Tip: Create a policy with a URL stringifier to verify scripts are from a trusted origin
+* Generating HTML from a string:
+    * ``innerHTML``, ``outerHTML``, ``insertAdjacentHTML``, ``<iframe> srcdoc``, ``document.write``, ``document.writeln``, and ``DOMParser.parseFromString``
+        * Tip: Use textContent instead of inner HTML
+        * Tip: Use a templating library that supports Trusted Types
+        * Tip: Use createElement and appendChild as explained above
+* Executing plugin content:
+    * ``<embed src>``, ``<object data>`` and ``<object codebase>``
+        * Tip: Consider limiting plugin content by setting``CSP_OBJECT_SRC`` to *none*
+* Runtime JavaScript code compilation:
+    * ``eval``, ``setTimeout``, ``setInterval``, and ``new Function()``
+        * Tip: Avoid using eval entirely
+        * Tip: Avoid passing strings to runtime compiled functions
+
+Use a Library
+-------------
+When code cannot be rewritten to avoid dangerous sinks, Trusted Types require
+that data be processed before being passed to a dangerous sink. Processed data
+is wrapped in a TrustedHTML, TrustedScript, or TrustedScriptURL object to certify that
+it has been sanitized or otherwise assured to be safe in the given context. Some libraries will process data and return Trusted
+Types objects for you. For example, DOMPurify_ supports Trusted Types.
+
+.. note::
+   Libraries are preferred to writing your own sanitation policies since they
+   are generally more comprehensive, secure, and well reviewed.
+
+Create Trusted Types Policies
+-----------------------------
+Where code cannot be rewritten and an existing library cannot be used, you will
+have to create Trusted Types objects yourself. This is done using policies. Different policies can be created for use in different contexts.
+Policies produce Trusted Types after enforcing security rules on their input
+based on the sink context. Each policy should be given a distinct name.
+
+Here is an example policy that sanitizes HTML by escaping the ``<`` character.
+
+.. code-block:: javascript
+
+	if (window.trustedTypes && trustedTypes.createPolicy) {
+    	const escapeHTMLPolicy = trustedTypes.createPolicy('myEscapePolicy', {
+    		createHTML: string => string.replace(/\</g, '&lt;')
+  		});
+	}
+
+Here is an example of how that policy can be used.
+
+.. code-block:: javascript
+
+	const escaped = escapeHTMLPolicy.createHTML('<img src=x onerror=alert(1)>');
+	console.log(escaped instanceof TrustedHTML);
+	el.innerHTML = escaped;
+
+.. note::
+   Keep in mind that you are creating your own security rules with policies.
+   Your application is only protected from DOM XSS if you use strict sanitation
+   rules that consider which sink is accepting the data.
+
+Use a Default Policy
+--------------------
+In the event that you don’t have control over the offending code, you can use a
+default policy. This may happen if you are loading a third party library that
+is not Trusted Types compliant. A default policy is defined the same way as any
+other Trusted Types policy. In order to be used by the browser as the default
+policy it must be named *default*.
+
+The policy called *default* will be used wherever a string is sent to a
+dangerous sink that requires Trusted Types.
+
+
+``Step 3: Enforce Trusted Types``
+=================================
+Once you have addressed all of the Trusted Types violations present in your
+application, you can begin enforcing Trusted Types to prevent DOM XSS. 
+
+Configure django-csp so that ``CSP_REPORT_ONLY`` is set to *False*.
+
+.. note::
+   To learn more about trusted types or learn how to limit policy creation with
+   ``CSP_TRUSTED_TYPES`` take a look at the complete spec_ or the article_ this
+   guide is based on.
+
+
+
+.. _DOM-XSS: https://owasp.org/www-community/attacks/xss/
+.. _compatibility: https://developer.mozilla.org/es/docs/Web/HTTP/Headers/Content-Security-Policy/trusted-types#Browser_compatibility
+.. _DOMPurify: https://github.com/cure53/DOMPurify#what-about-dompurify-and-trusted-types
+.. _spec: https://w3c.github.io/webappsec-trusted-types/dist/spec/
+.. _article: https://web.dev/trusted-types/


Test patch:
diff --git a/csp/tests/test_utils.py b/csp/tests/test_utils.py
index 4bb0659..87b8908 100644
--- a/csp/tests/test_utils.py
+++ b/csp/tests/test_utils.py
@@ -249,6 +249,20 @@ def test_require_sri_for():
     policy_eq("default-src 'self'; require-sri-for script", policy)
 
 
+@override_settings(CSP_REQUIRE_TRUSTED_TYPES_FOR=["'script'"])
+def test_require_trusted_types_for():
+    policy = build_policy()
+    policy_eq("default-src 'self'; require-trusted-types-for 'script'", policy)
+
+
+@override_settings(CSP_TRUSTED_TYPES=["strictPolicy", "laxPolicy",
+                                      "default", "'allow-duplicates'"])
+def test_trusted_types():
+    policy = build_policy()
+    policy_eq("default-src 'self'; trusted-types strictPolicy laxPolicy "
+              + "default 'allow-duplicates'", policy)
+
+
 @override_settings(CSP_UPGRADE_INSECURE_REQUESTS=True)
 def test_upgrade_insecure_requests():
     policy = build_policy()