summaryrefslogtreecommitdiff
path: root/docs/ref
diff options
context:
space:
mode:
authorRob Hudson <rob@cogit8.org>2025-05-03 10:01:58 -0700
committernessita <124304+nessita@users.noreply.github.com>2025-06-27 15:57:02 -0300
commitd63241ebc7067fdebbaf704989b34fcd8f26bbe9 (patch)
tree07b5a5cb0c70c446f5f0fb9ad2834501fc3d6544 /docs/ref
parent3f59711581bd22ebd0f13fb040b15b69c0eee21f (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.txt2
-rw-r--r--docs/ref/csp.txt210
-rw-r--r--docs/ref/index.txt1
-rw-r--r--docs/ref/middleware.txt26
-rw-r--r--docs/ref/settings.txt90
-rw-r--r--docs/ref/templates/api.txt12
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``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~