1
0
mirror of https://github.com/django/django.git synced 2025-10-24 14:16:09 +00:00

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>
This commit is contained in:
Rob Hudson
2025-05-03 10:01:58 -07:00
committed by nessita
parent 3f59711581
commit d63241ebc7
26 changed files with 1192 additions and 1 deletions

View File

@@ -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:

210
docs/ref/csp.txt Normal file
View File

@@ -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.

View File

@@ -10,6 +10,7 @@ API Reference
class-based-views/index
clickjacking
contrib/index
csp
csrf
databases
django-admin

View File

@@ -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.

View File

@@ -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`

View File

@@ -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``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~