diff options
| author | Rob Hudson <rob@cogit8.org> | 2025-05-03 10:01:58 -0700 |
|---|---|---|
| committer | nessita <124304+nessita@users.noreply.github.com> | 2025-06-27 15:57:02 -0300 |
| commit | d63241ebc7067fdebbaf704989b34fcd8f26bbe9 (patch) | |
| tree | 07b5a5cb0c70c446f5f0fb9ad2834501fc3d6544 /docs/ref | |
| parent | 3f59711581bd22ebd0f13fb040b15b69c0eee21f (diff) | |
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>
Diffstat (limited to 'docs/ref')
| -rw-r--r-- | docs/ref/checks.txt | 2 | ||||
| -rw-r--r-- | docs/ref/csp.txt | 210 | ||||
| -rw-r--r-- | docs/ref/index.txt | 1 | ||||
| -rw-r--r-- | docs/ref/middleware.txt | 26 | ||||
| -rw-r--r-- | docs/ref/settings.txt | 90 | ||||
| -rw-r--r-- | docs/ref/templates/api.txt | 12 |
6 files changed, 341 insertions, 0 deletions
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 ``<SETTING_NAME>`` must be a dictionary + (got ``<value>`` 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 +<security-csp>`. + +For configuration instructions in a Django project, see the :ref:`Using CSP +<csp-config>` documentation. For an HTTP guide about CSP, see the `MDN Guide on +CSP <https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/CSP>`_. + +.. _csp-overview: + +Overview +======== + +The `Content-Security-Policy specification <https://www.w3.org/TR/CSP3/>`_ +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 <csp-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 +<https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Content-Security-Policy#reporting_directives>`_ +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 (``"<CSP_NONCE_SENTINEL>"``) 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 +``<script>`` or ``<style>`` elements to execute without relying on +``'unsafe-inline'``. + +Nonces are enabled by including the special placeholder +:attr:`~django.utils.csp.CSP.NONCE` in the relevant directive(s) of your +:ref:`CSP settings <csp-settings>`, such as ``script-src`` or ``style-src``. +When present, the +:class:`~django.middleware.csp.ContentSecurityPolicyMiddleware` +will generate a nonce and insert the corresponding ``nonce-<value>`` source +expression into the CSP header. + +To use this nonce in templates, the +:func:`~django.template.context_processors.csp` context processor needs to be +enabled. It adds a ``csp_nonce`` variable to the template context, allowing +inline elements to include a matching ``nonce={{ csp_nonce }}`` attribute in +inline scripts or styles. + +The browser will only execute inline elements that include a ``nonce=<value>`` +attribute matching the one specified in the ``Content-Security-Policy`` (or +``Content-Security-Policy-Report-Only``) header. This mechanism provides +fine-grained control over which inline code is allowed to run. + +If a template includes ``{{ csp_nonce }}`` but the policy does not include +:attr:`~django.utils.csp.CSP.NONCE`, the HTML will include a nonce attribute, +but the header will lack the required source expression. In this case, the +browser will block the inline script or style (or report it for report-only +configurations). + +Nonce generation and caching +---------------------------- + +Django's nonce generation is **lazy**: the middleware only generates a nonce if +``{{ csp_nonce }}`` is accessed during template rendering. This avoids +unnecessary work for pages that do not use nonces. + +However, because nonces must be unique per request, extra care is needed when +using full-page caching (e.g., Django's cache middleware, CDN caching). Serving +cached responses with previously generated nonces may result in reuse across +users and requests. Although such responses may still appear to work (since the +nonce in the CSP header and HTML content match), reuse defeats the purpose of +the nonce and weakens security. + +To ensure nonce-based policies remain effective: + +* Avoid caching full responses that include ``{{ csp_nonce }}``. +* If caching is necessary, use a strategy that injects a fresh nonce on each + request, or consider refactoring your application to avoid inline scripts and + styles altogether. diff --git a/docs/ref/index.txt b/docs/ref/index.txt index 8fc99ada81..3741b82aad 100644 --- a/docs/ref/index.txt +++ b/docs/ref/index.txt @@ -10,6 +10,7 @@ API Reference class-based-views/index clickjacking contrib/index + csp csrf databases django-admin diff --git a/docs/ref/middleware.txt b/docs/ref/middleware.txt index e09df637f1..de78ef833f 100644 --- a/docs/ref/middleware.txt +++ b/docs/ref/middleware.txt @@ -607,6 +607,26 @@ You can add Cross Site Request Forgery protection to individual views using the Simple :doc:`clickjacking protection via the X-Frame-Options header </ref/clickjacking/>`. +Content Security Policy middleware +---------------------------------- + +.. currentmodule:: django.middleware.csp + +.. class:: ContentSecurityPolicyMiddleware + +.. versionadded:: 6.0 + +Adds support for Content Security Policy (CSP), which helps mitigate risks such +as Cross-Site Scripting (XSS) and data injection attacks by controlling the +sources of content that can be loaded in the browser. See the +:ref:`csp-overview` documentation for details on configuring policies. + +This middleware sets the following headers on the response depending on the +available settings: + +* ``Content-Security-Policy``, based on :setting:`SECURE_CSP`. +* ``Content-Security-Policy-Report-Only``, based on :setting:`SECURE_CSP_REPORT_ONLY`. + .. _middleware-ordering: Middleware ordering @@ -691,6 +711,12 @@ Here are some hints about the ordering of various Django middleware classes: After any middleware that modifies the ``Vary`` header: that header is used to pick a value for the cache hash-key. +#. :class:`~django.middleware.csp.ContentSecurityPolicyMiddleware` + + Can be placed near the bottom, but ensure any middleware that accesses + :ref:`csp_nonce <csp-nonce>` is positioned after it, so the nonce is + properly included in the response header. + #. :class:`~django.contrib.flatpages.middleware.FlatpageFallbackMiddleware` Should be near the bottom as it's a last-resort type of middleware. diff --git a/docs/ref/settings.txt b/docs/ref/settings.txt index e6fa3f4221..133916f60a 100644 --- a/docs/ref/settings.txt +++ b/docs/ref/settings.txt @@ -2363,6 +2363,94 @@ Unless set to ``None``, the :ref:`cross-origin-opener-policy` header on all responses that do not already have it to the value provided. +.. setting:: SECURE_CSP + +``SECURE_CSP`` +-------------- + +.. versionadded:: 6.0 + +Default: ``{}`` + +This setting defines the directives used by the +:class:`~django.middleware.csp.ContentSecurityPolicyMiddleware`, which +generates and adds a :ref:`Content-Security-Policy <csp-overview>` (CSP) header +to all responses that do not already include one. + +The ``Content-Security-Policy`` header instructs browsers to restrict which +resources a page is allowed to load. A properly configured CSP can block +content that violates defined rules, helping prevent cross-site scripting (XSS) +and other content injection attacks by explicitly declaring trusted sources for +content such as scripts, styles, images, fonts, and more. + +The setting must be a mapping (typically a dictionary) of directive names to +their values. Each key should be a valid CSP directive such as ``default-src`` +or ``script-src``. The corresponding value can be a list, tuple, or set of +source expressions or URLs to allow for that directive. If a set is used, it +will be automatically sorted to ensure consistent output in the generated +headers. + +This example illustrates the expected structure, using the constants defined in +:ref:`csp-constants`:: + + from django.utils.csp import CSP + + SECURE_CSP = { + "default-src": [CSP.SELF], + "img-src": ["data:", CSP.SELF, "https://images.example.com"], + "frame-src": [CSP.NONE], + } + +.. admonition:: Directives validation + + Django's CSP middleware helps construct and send the appropriate header + based on your settings, but it does **not validate** that the directives and + values conform to the CSP specification. It is your responsibility to ensure + that the configuration is syntactically and semantically correct. Use + browser developer tools or external CSP validators during development. + + For a list of available directives and their values, refer to the `MDN + documentation on CSP directives + <https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy#directives>`_. + + +.. setting:: SECURE_CSP_REPORT_ONLY + +``SECURE_CSP_REPORT_ONLY`` +-------------------------- + +.. versionadded:: 6.0 + +Default: ``{}`` + +This setting is just like :setting:`SECURE_CSP`, but instead of enforcing the +policy, it instructs the +:class:`~django.middleware.csp.ContentSecurityPolicyMiddleware` to apply a +``Content-Security-Policy-Report-Only`` header to responses, which allows +browsers to monitor and report policy violations without blocking content. This +is useful for testing and refining a policy before enforcement. + +Most browsers log CSP violations to the developer console and can optionally +send them to a reporting endpoint. To collect these reports, the ``report-uri`` +directive must be defined (see :ref:`csp-reports` for more details). + +As noted in the `MDN documentation on Content-Security-Policy-Report-Only +<https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy-Report-Only>`_, +the ``report-uri`` directive must be specified for reports to be sent; +otherwise, the header has no reporting effect (other than logging to the +browser's developer tools console). + +Following the example from the :setting:`SECURE_CSP` setting:: + + from django.utils.csp import CSP + + SECURE_CSP_REPORT_ONLY = { + "default-src": [CSP.SELF], + "img-src": ["data:", CSP.SELF, "https://images.example.com"], + "frame-src": [CSP.NONE], + "report-uri": "/my-site/csp/reports/", + } + .. setting:: SECURE_HSTS_INCLUDE_SUBDOMAINS ``SECURE_HSTS_INCLUDE_SUBDOMAINS`` @@ -3749,6 +3837,8 @@ HTTP * :setting:`SECURE_CONTENT_TYPE_NOSNIFF` * :setting:`SECURE_CROSS_ORIGIN_OPENER_POLICY` + * :setting:`SECURE_CSP` + * :setting:`SECURE_CSP_REPORT_ONLY` * :setting:`SECURE_HSTS_INCLUDE_SUBDOMAINS` * :setting:`SECURE_HSTS_PRELOAD` * :setting:`SECURE_HSTS_SECONDS` diff --git a/docs/ref/templates/api.txt b/docs/ref/templates/api.txt index 8d5c66367d..f1fb70c9b8 100644 --- a/docs/ref/templates/api.txt +++ b/docs/ref/templates/api.txt @@ -802,6 +802,18 @@ This processor adds a token that is needed by the :ttag:`csrf_token` template tag for protection against :doc:`Cross Site Request Forgeries </ref/csrf>`. +``django.template.context_processors.csp`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. function:: csp(request) + +.. versionadded:: 6.0 + +If this processor is enabled, every ``RequestContext`` will contain a variable +``csp_nonce``, providing a securely generated, request-specific nonce suitable +for use under a Content Security Policy. See :ref:`CSP nonce usage <csp-nonce>` +for details. + ``django.template.context_processors.request`` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
