From d63241ebc7067fdebbaf704989b34fcd8f26bbe9 Mon Sep 17 00:00:00 2001 From: Rob Hudson Date: Sat, 3 May 2025 10:01:58 -0700 Subject: Fixed #15727 -- Added Content Security Policy (CSP) support. This initial work adds a pair of settings to configure specific CSP directives for enforcing or reporting policy violations, a new `django.middleware.csp.ContentSecurityPolicyMiddleware` to apply the appropriate headers to responses, and a context processor to support CSP nonces in templates for safely inlining assets. Relevant documentation has been added for the 6.0 release notes, security overview, a new how-to page, and a dedicated reference section. Thanks to the multiple reviewers for their precise and valuable feedback. Co-authored-by: Natalia <124304+nessita@users.noreply.github.com> --- docs/howto/csp.txt | 100 +++++++++++++++++++++ docs/howto/index.txt | 1 + docs/index.txt | 1 + docs/ref/checks.txt | 2 + docs/ref/csp.txt | 210 +++++++++++++++++++++++++++++++++++++++++++++ docs/ref/index.txt | 1 + docs/ref/middleware.txt | 26 ++++++ docs/ref/settings.txt | 90 +++++++++++++++++++ docs/ref/templates/api.txt | 12 +++ docs/releases/6.0.txt | 37 ++++++++ docs/spelling_wordlist | 1 + docs/topics/security.txt | 55 ++++++++++++ 12 files changed, 536 insertions(+) create mode 100644 docs/howto/csp.txt create mode 100644 docs/ref/csp.txt (limited to 'docs') diff --git a/docs/howto/csp.txt b/docs/howto/csp.txt new file mode 100644 index 0000000000..756f815bf2 --- /dev/null +++ b/docs/howto/csp.txt @@ -0,0 +1,100 @@ +=========================================== +How to use Django's Content Security Policy +=========================================== + +.. _csp-config: + +Basic config +============ + +To enable Content Security Policy (CSP) in your Django project: + +1. Add the CSP middleware to your :setting:`MIDDLEWARE` setting:: + + MIDDLEWARE = [ + # ... + "django.middleware.csp.ContentSecurityPolicyMiddleware", + # ... + ] + +2. Configure the CSP policies in your ``settings.py`` using either + :setting:`SECURE_CSP` or :setting:`SECURE_CSP_REPORT_ONLY` (or both). The + :ref:`CSP Settings docs ` provide more details about the + differences between these two:: + + from django.utils.csp import CSP + + # To enforce a CSP policy: + SECURE_CSP = { + "default-src": [CSP.SELF], + # Add more directives to be enforced. + } + + # Or for report-only mode: + SECURE_CSP_REPORT_ONLY = { + "default-src": [CSP.SELF], + # Add more directives as needed. + "report-uri": "/path/to/reports-endpoint/", + } + +.. _csp-nonce-config: + +Nonce config +============ + +To use nonces in your CSP policy, beside the basic config, you need to: + +1. Include the :attr:`~django.utils.csp.CSP.NONCE` placeholder value in the CSP + settings. This only applies to ``script-src`` or ``style-src`` directives:: + + from django.utils.csp import CSP + + SECURE_CSP = { + "default-src": [CSP.SELF], + # Allow self-hosted scripts and script tags with matching `nonce` attr. + "script-src": [CSP.SELF, CSP.NONCE], + # Example of the less secure 'unsafe-inline' option. + "style-src": [CSP.SELF, CSP.UNSAFE_INLINE], + } + +2. Add the :func:`~django.template.context_processors.csp` context processor to + your :setting:`TEMPLATES` setting. This makes the generated nonce value + available in the Django templates as the ``csp_nonce`` context variable:: + + TEMPLATES = [ + { + "BACKEND": "django.template.backends.django.DjangoTemplates", + "OPTIONS": { + "context_processors": [ + # ... + "django.template.context_processors.csp", + ], + }, + }, + ] + +3. In your templates, add the ``nonce`` attribute to the relevant inline + `` + + + +.. admonition:: Caching and Nonce Reuse + + The :class:`~django.middleware.csp.ContentSecurityPolicyMiddleware` + automatically handles generating a unique nonce and inserting the + appropriate ``nonce-`` source expression into the + ``Content-Security-Policy`` (or ``Content-Security-Policy-Report-Only``) + header when the nonce is used in a template. + + To ensure correct behavior, make sure both the HTML and the header are + generated within the same request and not served from cache. See the + reference documentation on :ref:`csp-nonce` for implementation details and + important caching considerations. diff --git a/docs/howto/index.txt b/docs/howto/index.txt index d49a9b1206..00acf5c837 100644 --- a/docs/howto/index.txt +++ b/docs/howto/index.txt @@ -57,6 +57,7 @@ Other guides :maxdepth: 1 auth-remote-user + csp csrf custom-file-storage custom-management-commands diff --git a/docs/index.txt b/docs/index.txt index 358c465df5..330e191e1c 100644 --- a/docs/index.txt +++ b/docs/index.txt @@ -251,6 +251,7 @@ applications and Django provides multiple protection tools and mechanisms: * :doc:`Cross Site Request Forgery protection ` * :doc:`Cryptographic signing ` * :ref:`Security Middleware ` +* :doc:`Content Security Policy ` Internationalization and localization ===================================== diff --git a/docs/ref/checks.txt b/docs/ref/checks.txt index b9cb1d19cf..bb54dbdb98 100644 --- a/docs/ref/checks.txt +++ b/docs/ref/checks.txt @@ -568,6 +568,8 @@ The following checks are run if you use the :option:`check --deploy` option: ``'django-insecure-'`` indicating that it was generated automatically by Django. Please generate a long and random value, otherwise many of Django's security-critical features will be vulnerable to attack. +* **security.E026**: The CSP setting ```` must be a dictionary + (got ```` instead). The following checks verify that your security-related settings are correctly configured: diff --git a/docs/ref/csp.txt b/docs/ref/csp.txt new file mode 100644 index 0000000000..e3666c9129 --- /dev/null +++ b/docs/ref/csp.txt @@ -0,0 +1,210 @@ +======================= +Content Security Policy +======================= + +.. versionadded:: 6.0 + +.. module:: django.middleware.csp + :synopsis: Middleware for Content Security Policy headers + +Content Security Policy (CSP) is a web security standard that helps prevent +content injection attacks by restricting the sources from which content can be +loaded. It plays an important role in a comprehensive :ref:`security strategy +`. + +For configuration instructions in a Django project, see the :ref:`Using CSP +` documentation. For an HTTP guide about CSP, see the `MDN Guide on +CSP `_. + +.. _csp-overview: + +Overview +======== + +The `Content-Security-Policy specification `_ +defines two complementary headers: + +* ``Content-Security-Policy``: Enforces the CSP policy, blocking content that + violates the defined directives. +* ``Content-Security-Policy-Report-Only``: Reports CSP violations without + blocking content, allowing for non-intrusive testing. + +Each policy is composed of one or more directives and their values, which +together instruct the browser on how to handle specific types of content. + +When the :class:`~django.middleware.csp.ContentSecurityPolicyMiddleware` is +enabled, Django automatically builds and attaches the appropriate headers to +each response based on the configured :ref:`settings `, unless +they have already been set by another layer. + +.. _csp-settings: + +Settings +======== + +The :class:`~django.middleware.csp.ContentSecurityPolicyMiddleware` is +configured using the following settings: + +* :setting:`SECURE_CSP`: defines the **enforced Content Security Policy**. +* :setting:`SECURE_CSP_REPORT_ONLY`: defines a **report-only Content Security Policy**. + +.. admonition:: These settings can be used independently or together + + * Use :setting:`SECURE_CSP` alone to enforce a policy that has already been + tested and verified. + * Use :setting:`SECURE_CSP_REPORT_ONLY` on its own to evaluate a new policy + without disrupting site behavior. This mode does not block violations, it + only logs them. It's useful for testing and monitoring, but provides no + protection against active threats. + * Use *both* to maintain an enforced baseline while experimenting with + changes. Even for well-established policies, continuing to collect reports + reports can help detect regressions, unexpected changes in behavior, or + potential tampering in production environments. + +.. _csp-reports: + +Policy violation reports +======================== + +When a CSP violation occurs, browsers typically log details to the developer +console, providing immediate feedback during development. To also receive these +reports programmatically, the policy must include a `reporting directive +`_ +such as ``report-uri`` that specifies where violation data should be sent. + +Django supports configuring these directives via the +:setting:`SECURE_CSP_REPORT_ONLY` settings, but reports will only be issued by +the browser if the policy explicitly includes a valid reporting directive. + +Django does not provide built-in functionality to receive, store, or process +violation reports. To collect and analyze them, you must implement your own +reporting endpoint or integrate with a third-party monitoring service. + +.. _csp-constants: + +CSP constants +============= + +Django provides predefined constants representing common CSP source expression +keywords such as ``'self'``, ``'none'``, and ``'unsafe-inline'``. These +constants are intended for use in the directive values defined in the settings. + +They are available through the :class:`~django.utils.csp.CSP` enum, and using +them is recommended over raw strings. This helps avoid common mistakes such as +typos, improper quoting, or inconsistent formatting, and ensures compliance +with the CSP specification. + +.. module:: django.utils.csp + :synopsis: Constants for Content Security Policy + +.. class:: CSP + + Enum providing standardized constants for common CSP source expressions. + + .. attribute:: NONE + + Represents ``'none'``. Blocks loading resources for the given directive. + + .. attribute:: REPORT_SAMPLE + + Represents ``'report-sample'``. Instructs the browser to include a sample + of the violating code in reports. Note that this may expose sensitive + data. + + .. attribute:: SELF + + Represents ``'self'``. Allows loading resources from the same origin + (same scheme, host, and port). + + .. attribute:: STRICT_DYNAMIC + + Represents ``'strict-dynamic'``. Allows execution of scripts loaded by a + trusted script (e.g., one with a valid nonce or hash), without needing + ``'unsafe-inline'``. + + .. attribute:: UNSAFE_EVAL + + Represents ``'unsafe-eval'``. Allows use of ``eval()`` and similar + JavaScript functions. Strongly discouraged. + + .. attribute:: UNSAFE_HASHES + + Represents ``'unsafe-hashes'``. Allows inline event handlers and some + ``javascript:`` URIs when their content hashes match a policy rule. + Requires CSP Level 3+. + + .. attribute:: UNSAFE_INLINE + + Represents ``'unsafe-inline'``. Allows execution of inline scripts, + styles, and ``javascript:`` URLs. Generally discouraged, especially for + scripts. + + .. attribute:: WASM_UNSAFE_EVAL + + Represents ``'wasm-unsafe-eval'``. Permits compilation and execution of + WebAssembly code without enabling ``'unsafe-eval'`` for scripts. + + .. attribute:: NONCE + + Django-specific placeholder value (``""``) used in + ``script-src`` or ``style-src`` directives to activate nonce-based CSP. + This string is replaced at runtime by the + :class:`~django.middleware.csp.ContentSecurityPolicyMiddleware` with a + secure, random nonce that is generated for each request. See detailed + explanation in :ref:`csp-nonce`. + +.. _csp-nonce: + +Nonce usage +=========== + +A CSP nonce ("number used once") is a unique, random value generated per HTTP +response. Django supports nonces as a secure way to allow specific inline +``