Fixed #17101 -- Integrated django-secure and added check --deploy option

Thanks Carl Meyer for django-secure and for reviewing.

Thanks also to Zach Borboa, Erik Romijn, Collin Anderson, and
Jorge Carleitao for reviews.
This commit is contained in:
Tim Graham 2014-09-12 14:50:36 -04:00
parent 8f334e55be
commit 52ef6a4726
24 changed files with 1638 additions and 22 deletions

View file

@ -29,6 +29,14 @@ you're releasing the source code for your project, a common practice is to
publish suitable settings for development, and to use a private settings
module for production.
Run ``manage.py check --deploy``
================================
Some of the checks described below can be automated using the
:djadminopt:`--deploy` option of the :djadmin:`check` command. Be sure to run it
against your production settings file as described in the option's
documentation.
Critical settings
=================

View file

@ -229,6 +229,7 @@ applications and Django provides multiple protection tools and mechanisms:
* :doc:`Clickjacking protection <ref/clickjacking>`
* :doc:`Cross Site Request Forgery protection <ref/contrib/csrf>`
* :doc:`Cryptographic signing <topics/signing>`
* :ref:`Security Middleware <security-middleware>`
Internationalization and localization
=====================================

View file

@ -20,6 +20,7 @@ Django's system checks are organized using the following tags:
* ``signals``: Checks on signal declarations and handler registrations.
* ``admin``: Checks of any admin site declarations.
* ``compatibility``: Flagging potential problems with version upgrades.
* ``security``: Checks security related configuration.
Some checks may be registered with multiple tags.
@ -346,6 +347,110 @@ The following checks are performed when a model contains a
* **contenttypes.E004**: ``<field>`` is not a ``ForeignKey`` to
``contenttypes.ContentType``.
Security
--------
The security checks do not make your site secure. They do not audit code, do
intrusion detection, or do anything particularly complex. Rather, they help
perform an automated, low-hanging-fruit checklist. They help you remember the
simple things that improve your site's security.
Some of these checks may not be appropriate for your particular deployment
configuration. For instance, if you do your HTTP to HTTPS redirection in a load
balancer, it'd be irritating to be constantly warned about not having enabled
:setting:`SECURE_SSL_REDIRECT`. Use :setting:`SILENCED_SYSTEM_CHECKS` to
silence unneeded checks.
The following checks will be run if you use the :djadminopt:`--deploy` option
of the :djadmin:`check` command:
* **security.W001**: You do not have
:class:`django.middleware.security.SecurityMiddleware` in your
:setting:`MIDDLEWARE_CLASSES` so the :setting:`SECURE_HSTS_SECONDS`,
:setting:`SECURE_CONTENT_TYPE_NOSNIFF`, :setting:`SECURE_BROWSER_XSS_FILTER`,
and :setting:`SECURE_SSL_REDIRECT` settings will have no effect.
* **security.W002**: You do not have
:class:`django.middleware.clickjacking.XFrameOptionsMiddleware` in your
:setting:`MIDDLEWARE_CLASSES`, so your pages will not be served with an
``'x-frame-options'`` header. Unless there is a good reason for your
site to be served in a frame, you should consider enabling this
header to help prevent clickjacking attacks.
* **security.W003**: You don't appear to be using Django's built-in cross-site
request forgery protection via the middleware
(:class:`django.middleware.csrf.CsrfViewMiddleware` is not in your
:setting:`MIDDLEWARE_CLASSES`). Enabling the middleware is the safest
approach to ensure you don't leave any holes.
* **security.W004**: You have not set a value for the
:setting:`SECURE_HSTS_SECONDS` setting. If your entire site is served only
over SSL, you may want to consider setting a value and enabling :ref:`HTTP
Strict Transport Security <http-strict-transport-security>`. Be sure to read
the documentation first; enabling HSTS carelessly can cause serious,
irreversible problems.
* **security.W005**: You have not set the
:setting:`SECURE_HSTS_INCLUDE_SUBDOMAINS` setting to ``True``. Without this,
your site is potentially vulnerable to attack via an insecure connection to a
subdomain. Only set this to ``True`` if you are certain that all subdomains of
your domain should be served exclusively via SSL.
* **security.W006**: Your :setting:`SECURE_CONTENT_TYPE_NOSNIFF` setting is not
set to ``True``, so your pages will not be served with an
``'x-content-type-options: nosniff'`` header. You should consider enabling
this header to prevent the browser from identifying content types incorrectly.
* **security.W007**: Your :setting:`SECURE_BROWSER_XSS_FILTER` setting is not
set to ``True``, so your pages will not be served with an
``'x-xss-protection: 1; mode=block'`` header. You should consider enabling
this header to activate the browser's XSS filtering and help prevent XSS
attacks.
* **security.W008**: Your :setting:`SECURE_SSL_REDIRECT` setting is not set to
``True``. Unless your site should be available over both SSL and non-SSL
connections, you may want to either set this setting to ``True`` or configure
a load balancer or reverse-proxy server to redirect all connections to HTTPS.
* **security.W009**: Your :setting:`SECRET_KEY` has less than 50 characters or
less than 5 unique characters. Please generate a long and random
``SECRET_KEY``, otherwise many of Django's security-critical features will be
vulnerable to attack.
* **security.W010**: You have :mod:`django.contrib.sessions` in your
:setting:`INSTALLED_APPS` but you have not set
:setting:`SESSION_COOKIE_SECURE` to ``True``. Using a secure-only session
cookie makes it more difficult for network traffic sniffers to hijack user
sessions.
* **security.W011**: You have
:class:`django.contrib.sessions.middleware.SessionMiddleware` in your
:setting:`MIDDLEWARE_CLASSES`, but you have not set
:setting:`SESSION_COOKIE_SECURE` to ``True``. Using a secure-only session
cookie makes it more difficult for network traffic sniffers to hijack user
sessions.
* **security.W012**: :setting:`SESSION_COOKIE_SECURE` is not set to ``True``.
Using a secure-only session cookie makes it more difficult for network traffic
sniffers to hijack user sessions.
* **security.W013**: You have :mod:`django.contrib.sessions` in your
:setting:`INSTALLED_APPS`, but you have not set
:setting:`SESSION_COOKIE_HTTPONLY` to ``True``. Using an ``HttpOnly`` session
cookie makes it more difficult for cross-site scripting attacks to hijack user
sessions.
* **security.W014**: You have
:class:`django.contrib.sessions.middleware.SessionMiddleware` in your
:setting:`MIDDLEWARE_CLASSES`, but you have not set
:setting:`SESSION_COOKIE_HTTPONLY` to ``True``. Using an ``HttpOnly`` session
cookie makes it more difficult for cross-site scripting attacks to hijack user
sessions.
* **security.W015**: :setting:`SESSION_COOKIE_HTTPONLY` is not set to ``True``.
Using an ``HttpOnly`` session cookie makes it more difficult for cross-site
scripting attacks to hijack user sessions.
* **security.W016**: :setting:`CSRF_COOKIE_SECURE` is not set to ``True``.
Using a secure-only CSRF cookie makes it more difficult for network traffic
sniffers to steal the CSRF token.
* **security.W017**: :setting:`CSRF_COOKIE_HTTPONLY` is not set to ``True``.
Using an ``HttpOnly`` CSRF cookie makes it more difficult for cross-site
scripting attacks to steal the CSRF token.
* **security.W018**: You should not have :setting:`DEBUG` set to ``True`` in
deployment.
* **security.W019**: You have
:class:`django.middleware.clickjacking.XFrameOptionsMiddleware` in your
:setting:`MIDDLEWARE_CLASSES`, but :setting:`X_FRAME_OPTIONS` is not set to
``'DENY'``. The default is ``'SAMEORIGIN'``, but unless there is a good reason
for your site to serve other parts of itself in a frame, you should change
it to ``'DENY'``.
Sites
-----

View file

@ -135,6 +135,25 @@ to perform only security and compatibility checks, you would run::
List all available tags.
.. django-admin-option:: --deploy
.. versionadded:: 1.8
The ``--deploy`` option activates some additional checks that are only relevant
in a deployment setting.
You can use this option in your local development environment, but since your
local development settings module may not have many of your production settings,
you will probably want to point the ``check`` command at a different settings
module, either by setting the ``DJANGO_SETTINGS_MODULE`` environment variable,
or by passing the ``--settings`` option::
python manage.py check --deploy --settings=production_settings
Or you could run it directly on a production or staging deployment to verify
that the correct settings are in use (omitting ``--settings``). You could even
make it part of your integration test suite.
compilemessages
---------------

View file

@ -155,6 +155,178 @@ Message middleware
Enables cookie- and session-based message support. See the
:doc:`messages documentation </ref/contrib/messages>`.
.. _security-middleware:
Security middleware
-------------------
.. module:: django.middleware.security
:synopsis: Security middleware.
.. warning::
If your deployment situation allows, it's usually a good idea to have your
front-end Web server perform the functionality provided by the
``SecurityMiddleware``. That way, if there are requests that aren't served
by Django (such as static media or user-uploaded files), they will have
the same protections as requests to your Django application.
.. class:: SecurityMiddleware
.. versionadded:: 1.8
The ``django.middleware.security.SecurityMiddleware`` provides several security
enhancements to the request/response cycle. Each one can be independently
enabled or disabled with a setting.
* :setting:`SECURE_BROWSER_XSS_FILTER`
* :setting:`SECURE_CONTENT_TYPE_NOSNIFF`
* :setting:`SECURE_HSTS_INCLUDE_SUBDOMAINS`
* :setting:`SECURE_HSTS_SECONDS`
* :setting:`SECURE_REDIRECT_EXEMPT`
* :setting:`SECURE_SSL_HOST`
* :setting:`SECURE_SSL_REDIRECT`
.. _http-strict-transport-security:
HTTP Strict Transport Security
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
For sites that should only be accessed over HTTPS, you can instruct modern
browsers to refuse to connect to your domain name via an insecure connection
(for a given period of time) by setting the `"Strict-Transport-Security"
header`_. This reduces your exposure to some SSL-stripping man-in-the-middle
(MITM) attacks.
``SecurityMiddleware`` will set this header for you on all HTTPS responses if
you set the :setting:`SECURE_HSTS_SECONDS` setting to a non-zero integer value.
When enabling HSTS, it's a good idea to first use a small value for testing,
for example, :setting:`SECURE_HSTS_SECONDS = 3600<SECURE_HSTS_SECONDS>` for one
hour. Each time a Web browser sees the HSTS header from your site, it will
refuse to communicate non-securely (using HTTP) with your domain for the given
period of time. Once you confirm that all assets are served securely on your
site (i.e. HSTS didn't break anything), it's a good idea to increase this value
so that infrequent visitors will be protected (31536000 seconds, i.e. 1 year,
is common).
Additionally, if you set the :setting:`SECURE_HSTS_INCLUDE_SUBDOMAINS` setting
to ``True``, ``SecurityMiddleware`` will add the ``includeSubDomains`` tag to
the ``Strict-Transport-Security`` header. This is recommended (assuming all
subdomains are served exclusively using HTTPS), otherwise your site may still
be vulnerable via an insecure connection to a subdomain.
.. warning::
The HSTS policy applies to your entire domain, not just the URL of the
response that you set the header on. Therefore, you should only use it if
your entire domain is served via HTTPS only.
Browsers properly respecting the HSTS header will refuse to allow users to
bypass warnings and connect to a site with an expired, self-signed, or
otherwise invalid SSL certificate. If you use HSTS, make sure your
certificates are in good shape and stay that way!
.. note::
If you are deployed behind a load-balancer or reverse-proxy server, and the
``Strict-Transport-Security`` header is not being added to your responses,
it may be because Django doesn't realize that it's on a secure connection;
you may need to set the :setting:`SECURE_PROXY_SSL_HEADER` setting.
.. _"Strict-Transport-Security" header: http://en.wikipedia.org/wiki/Strict_Transport_Security
.. _x-content-type-options:
``X-Content-Type-Options: nosniff``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Some browsers will try to guess the content types of the assets that they
fetch, overriding the ``Content-Type`` header. While this can help display
sites with improperly configured servers, it can also pose a security
risk.
If your site serves user-uploaded files, a malicious user could upload a
specially-crafted file that would be interpreted as HTML or Javascript by
the browser when you expected it to be something harmless.
To learn more about this header and how the browser treats it, you can
read about it on the `IE Security Blog`_.
To prevent the browser from guessing the content type and force it to
always use the type provided in the ``Content-Type`` header, you can pass
the ``X-Content-Type-Options: nosniff`` header. ``SecurityMiddleware`` will
do this for all responses if the :setting:`SECURE_CONTENT_TYPE_NOSNIFF` setting
is ``True``.
Note that in most deployment situations where Django isn't involved in serving
user-uploaded files, this setting won't help you. For example, if your
:setting:`MEDIA_URL` is served directly by your front-end Web server (nginx,
Apache, etc.) then you'd want to set this header there. On the other hand, if
you are using Django to do something like require authorization in order to
download files and you cannot set the header using your Web server, this
setting will be useful.
.. _IE Security Blog: http://blogs.msdn.com/b/ie/archive/2008/09/02/ie8-security-part-vi-beta-2-update.aspx
.. _x-xss-protection:
``X-XSS-Protection: 1; mode=block``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Some browsers have the ability to block content that appears to be an `XSS
attack`_. They work by looking for Javascript content in the GET or POST
parameters of a page. If the Javascript is replayed in the server's response,
the page is blocked from rendering and an error page is shown instead.
The `X-XSS-Protection header`_ is used to control the operation of the
XSS filter.
To enable the XSS filter in the browser, and force it to always block
suspected XSS attacks, you can pass the ``X-XSS-Protection: 1; mode=block``
header. ``SecurityMiddleware`` will do this for all responses if the
:setting:`SECURE_BROWSER_XSS_FILTER` setting is ``True``.
.. warning::
The browser XSS filter is a useful defense measure, but must not be
relied upon exclusively. It cannot detect all XSS attacks and not all
browsers support the header. Ensure you are still :ref:`validating and
sanitizing <cross-site-scripting>` all input to prevent XSS attacks.
.. _XSS attack: http://en.wikipedia.org/wiki/Cross-site_scripting
.. _X-XSS-Protection header: http://blogs.msdn.com/b/ie/archive/2008/07/02/ie8-security-part-iv-the-xss-filter.aspx
.. _ssl-redirect:
SSL Redirect
~~~~~~~~~~~~
If your site offers both HTTP and HTTPS connections, most users will end up
with an unsecured connection by default. For best security, you should redirect
all HTTP connections to HTTPS.
If you set the :setting:`SECURE_SSL_REDIRECT` setting to True,
``SecurityMiddleware`` will permanently (HTTP 301) redirect all HTTP
connections to HTTPS.
.. note::
For performance reasons, it's preferable to do these redirects outside of
Django, in a front-end load balancer or reverse-proxy server such as
`nginx`_. :setting:`SECURE_SSL_REDIRECT` is intended for the deployment
situations where this isn't an option.
If the :setting:`SECURE_SSL_HOST` setting has a value, all redirects will be
sent to that host instead of the originally-requested host.
If there are a few pages on your site that should be available over HTTP, and
not redirected to HTTPS, you can list regular expressions to match those URLs
in the :setting:`SECURE_REDIRECT_EXEMPT` setting.
.. note::
If you are deployed behind a load-balancer or reverse-proxy server and
Django can't seem to tell when a request actually is already secure, you
may need to set the :setting:`SECURE_PROXY_SSL_HEADER` setting.
.. _nginx: http://nginx.org
Session middleware
------------------

View file

@ -357,6 +357,12 @@ Default: ``False``
Whether to use ``HttpOnly`` flag on the CSRF cookie. If this is set to
``True``, client-side JavaScript will not to be able to access the CSRF cookie.
This can help prevent malicious JavaScript from bypassing CSRF protection. If
you enable this and need to send the value of the CSRF token with Ajax requests,
your JavaScript will need to pull the value from a hidden CSRF token form input
on the page instead of from the cookie.
See :setting:`SESSION_COOKIE_HTTPONLY` for details on ``HttpOnly``.
.. setting:: CSRF_COOKIE_NAME
@ -1902,6 +1908,67 @@ Django will refuse to start if :setting:`SECRET_KEY` is not set.
security protections, and can lead to privilege escalation and remote code
execution vulnerabilities.
.. setting:: SECURE_BROWSER_XSS_FILTER
SECURE_BROWSER_XSS_FILTER
-------------------------
.. versionadded:: 1.8
Default: ``False``
If ``True``, the :class:`~django.middleware.security.SecurityMiddleware` sets
the :ref:`x-xss-protection` header on all responses that do not already have it.
.. setting:: SECURE_CONTENT_TYPE_NOSNIFF
SECURE_CONTENT_TYPE_NOSNIFF
---------------------------
.. versionadded:: 1.8
Default: ``False``
If ``True``, the :class:`~django.middleware.security.SecurityMiddleware`
sets the :ref:`x-content-type-options` header on all responses that do not
already have it.
.. setting:: SECURE_HSTS_INCLUDE_SUBDOMAINS
SECURE_HSTS_INCLUDE_SUBDOMAINS
------------------------------
.. versionadded:: 1.8
Default: ``False``
If ``True``, the :class:`~django.middleware.security.SecurityMiddleware` adds
the ``includeSubDomains`` tag to the :ref:`http-strict-transport-security`
header. It has no effect unless :setting:`SECURE_HSTS_SECONDS` is set to a
non-zero value.
.. warning::
Setting this incorrectly can irreversibly (for some time) break your site.
Read the :ref:`http-strict-transport-security` documentation first.
.. setting:: SECURE_HSTS_SECONDS
SECURE_HSTS_SECONDS
-------------------
.. versionadded:: 1.8
Default: ``0``
If set to a non-zero integer value, the
:class:`~django.middleware.security.SecurityMiddleware` sets the
:ref:`http-strict-transport-security` header on all responses that do not
already have it.
.. warning::
Setting this incorrectly can irreversibly (for some time) break your site.
Read the :ref:`http-strict-transport-security` documentation first.
.. setting:: SECURE_PROXY_SSL_HEADER
SECURE_PROXY_SSL_HEADER
@ -1963,6 +2030,55 @@ available in ``request.META``.)
If any of those are not true, you should keep this setting set to ``None``
and find another way of determining HTTPS, perhaps via custom middleware.
.. setting:: SECURE_REDIRECT_EXEMPT
SECURE_REDIRECT_EXEMPT
----------------------
.. versionadded:: 1.8
Default: ``[]``
If a URL path matches a regular expression in this list, the request will not be
redirected to HTTPS. If :setting:`SECURE_SSL_REDIRECT` is ``False``, this
setting has no effect.
.. setting:: SECURE_SSL_HOST
SECURE_SSL_HOST
---------------
.. versionadded:: 1.8
Default: ``None``
If a string (e.g. ``secure.example.com``), all SSL redirects will be directed
to this host rather than the originally-requested host
(e.g. ``www.example.com``). If :setting:`SECURE_SSL_REDIRECT` is ``False``, this
setting has no effect.
.. setting:: SECURE_SSL_REDIRECT
SECURE_SSL_REDIRECT
-------------------
.. versionadded:: 1.8
Default: ``False``.
If ``True``, the :class:`~django.middleware.security.SecurityMiddleware`
:ref:`redirects <ssl-redirect>` all non-HTTPS requests to HTTPS (except for
those URLs matching a regular expression listed in
:setting:`SECURE_REDIRECT_EXEMPT`).
.. note::
If turning this to ``True`` causes infinite redirects, it probably means
your site is running behind a proxy and can't tell which requests are secure
and which are not. Your proxy likely sets a header to indicate secure
requests; you can correct the problem by finding out what that header is and
configuring the :setting:`SECURE_PROXY_SSL_HEADER` setting accordingly.
.. setting:: SERIALIZATION_MODULES
SERIALIZATION_MODULES
@ -2642,6 +2758,11 @@ consistently by all browsers. However, when it is honored, it can be a
useful way to mitigate the risk of client side script accessing the
protected cookie data.
Turning it on makes it less trivial for an attacker to escalate a cross-site
scripting vulnerability into full hijacking of a user's session. There's not
much excuse for leaving this off, either: if your code depends on reading
session cookies from Javascript, you're probably doing it wrong.
.. versionadded:: 1.7
This setting also affects cookies set by :mod:`django.contrib.messages`.
@ -2683,6 +2804,13 @@ Whether to use a secure cookie for the session cookie. If this is set to
``True``, the cookie will be marked as "secure," which means browsers may
ensure that the cookie is only sent under an HTTPS connection.
Since it's trivial for a packet sniffer (e.g. `Firesheep`_) to hijack a user's
session if the session cookie is sent unencrypted, there's really no good
excuse to leave this off. It will prevent you from using sessions on insecure
requests and that's a good thing.
.. _Firesheep: http://codebutler.com/firesheep
.. versionadded:: 1.7
This setting also affects cookies set by :mod:`django.contrib.messages`.
@ -3023,7 +3151,16 @@ HTTP
* :setting:`FORCE_SCRIPT_NAME`
* :setting:`INTERNAL_IPS`
* :setting:`MIDDLEWARE_CLASSES`
* :setting:`SECURE_PROXY_SSL_HEADER`
* Security
* :setting:`SECURE_BROWSER_XSS_FILTER`
* :setting:`SECURE_CONTENT_TYPE_NOSNIFF`
* :setting:`SECURE_HSTS_INCLUDE_SUBDOMAINS`
* :setting:`SECURE_HSTS_SECONDS`
* :setting:`SECURE_PROXY_SSL_HEADER`
* :setting:`SECURE_REDIRECT_EXEMPT`
* :setting:`SECURE_SSL_HOST`
* :setting:`SECURE_SSL_REDIRECT`
* :setting:`SIGNING_BACKEND`
* :setting:`USE_ETAGS`
* :setting:`USE_X_FORWARDED_HOST`

View file

@ -23,7 +23,17 @@ Like Django 1.7, Django 1.8 requires Python 2.7 or above, though we
What's new in Django 1.8
========================
...
Security enhancements
~~~~~~~~~~~~~~~~~~~~~
Several features of the django-secure_ third-party library have been
integrated into Django. :class:`django.middleware.security.SecurityMiddleware`
provides several security enhancements to the request/response cycle. The new
:djadminopt:`--deploy` option of the :djadmin:`check` command allows you to
check your production settings file for ways to increase the security of your
site.
.. _django-secure: https://pypi.python.org/pypi/django-secure
Minor features
~~~~~~~~~~~~~~

View file

@ -203,6 +203,7 @@ filesizeformat
filesystem
filesystems
findstatic
Firesheep
firstof
fk
flatpage
@ -572,6 +573,7 @@ sqlmigrate
sqlsequencereset
squashmigrations
ssi
SSL
stacktrace
startswith
stateful
@ -742,6 +744,7 @@ www
xe
xgettext
xref
XSS
xxxxx
yesno
Zope

View file

@ -132,13 +132,25 @@ check. Tagging checks is useful since it allows you to run only a certain
group of checks. For example, to register a compatibility check, you would
make the following call::
from django.core.checks import register
from django.core.checks import register, Tags
@register('compatibility')
@register(Tags.compatibility)
def my_check(app_configs, **kwargs):
# ... perform compatibility checks and collect errors
return errors
.. versionadded:: 1.8
You can register "deployment checks" that are only relevant to a production
settings file like this::
@register(Tags.security, deploy=True)
def my_check(app_configs, **kwargs):
...
These checks will only be run if the :djadminopt:`--deploy` option is passed to
the :djadmin:`check` command.
.. _field-checking:
Field, Model, and Manager checks