mirror of
https://github.com/django/django.git
synced 2024-12-28 12:06:22 +00:00
52ef6a4726
Thanks Carl Meyer for django-secure and for reviewing. Thanks also to Zach Borboa, Erik Romijn, Collin Anderson, and Jorge Carleitao for reviews.
488 lines
16 KiB
Python
488 lines
16 KiB
Python
from django.conf import settings
|
|
from django.test import TestCase
|
|
from django.test.utils import override_settings
|
|
|
|
from django.core.checks.security import base
|
|
from django.core.checks.security import csrf
|
|
from django.core.checks.security import sessions
|
|
|
|
|
|
class CheckSessionCookieSecureTest(TestCase):
|
|
@property
|
|
def func(self):
|
|
from django.core.checks.security.sessions import check_session_cookie_secure
|
|
return check_session_cookie_secure
|
|
|
|
@override_settings(
|
|
SESSION_COOKIE_SECURE=False,
|
|
INSTALLED_APPS=["django.contrib.sessions"],
|
|
MIDDLEWARE_CLASSES=[])
|
|
def test_session_cookie_secure_with_installed_app(self):
|
|
"""
|
|
Warn if SESSION_COOKIE_SECURE is off and "django.contrib.sessions" is
|
|
in INSTALLED_APPS.
|
|
"""
|
|
self.assertEqual(self.func(None), [sessions.W010])
|
|
|
|
@override_settings(
|
|
SESSION_COOKIE_SECURE=False,
|
|
INSTALLED_APPS=[],
|
|
MIDDLEWARE_CLASSES=["django.contrib.sessions.middleware.SessionMiddleware"])
|
|
def test_session_cookie_secure_with_middleware(self):
|
|
"""
|
|
Warn if SESSION_COOKIE_SECURE is off and
|
|
"django.contrib.sessions.middleware.SessionMiddleware" is in
|
|
MIDDLEWARE_CLASSES.
|
|
"""
|
|
self.assertEqual(self.func(None), [sessions.W011])
|
|
|
|
@override_settings(
|
|
SESSION_COOKIE_SECURE=False,
|
|
INSTALLED_APPS=["django.contrib.sessions"],
|
|
MIDDLEWARE_CLASSES=["django.contrib.sessions.middleware.SessionMiddleware"])
|
|
def test_session_cookie_secure_both(self):
|
|
"""
|
|
If SESSION_COOKIE_SECURE is off and we find both the session app and
|
|
the middleware, provide one common warning.
|
|
"""
|
|
self.assertEqual(self.func(None), [sessions.W012])
|
|
|
|
@override_settings(
|
|
SESSION_COOKIE_SECURE=True,
|
|
INSTALLED_APPS=["django.contrib.sessions"],
|
|
MIDDLEWARE_CLASSES=["django.contrib.sessions.middleware.SessionMiddleware"])
|
|
def test_session_cookie_secure_true(self):
|
|
"""
|
|
If SESSION_COOKIE_SECURE is on, there's no warning about it.
|
|
"""
|
|
self.assertEqual(self.func(None), [])
|
|
|
|
|
|
class CheckSessionCookieHttpOnlyTest(TestCase):
|
|
@property
|
|
def func(self):
|
|
from django.core.checks.security.sessions import check_session_cookie_httponly
|
|
return check_session_cookie_httponly
|
|
|
|
@override_settings(
|
|
SESSION_COOKIE_HTTPONLY=False,
|
|
INSTALLED_APPS=["django.contrib.sessions"],
|
|
MIDDLEWARE_CLASSES=[])
|
|
def test_session_cookie_httponly_with_installed_app(self):
|
|
"""
|
|
Warn if SESSION_COOKIE_HTTPONLY is off and "django.contrib.sessions"
|
|
is in INSTALLED_APPS.
|
|
"""
|
|
self.assertEqual(self.func(None), [sessions.W013])
|
|
|
|
@override_settings(
|
|
SESSION_COOKIE_HTTPONLY=False,
|
|
INSTALLED_APPS=[],
|
|
MIDDLEWARE_CLASSES=["django.contrib.sessions.middleware.SessionMiddleware"])
|
|
def test_session_cookie_httponly_with_middleware(self):
|
|
"""
|
|
Warn if SESSION_COOKIE_HTTPONLY is off and
|
|
"django.contrib.sessions.middleware.SessionMiddleware" is in
|
|
MIDDLEWARE_CLASSES.
|
|
"""
|
|
self.assertEqual(self.func(None), [sessions.W014])
|
|
|
|
@override_settings(
|
|
SESSION_COOKIE_HTTPONLY=False,
|
|
INSTALLED_APPS=["django.contrib.sessions"],
|
|
MIDDLEWARE_CLASSES=["django.contrib.sessions.middleware.SessionMiddleware"])
|
|
def test_session_cookie_httponly_both(self):
|
|
"""
|
|
If SESSION_COOKIE_HTTPONLY is off and we find both the session app and
|
|
the middleware, provide one common warning.
|
|
"""
|
|
self.assertEqual(self.func(None), [sessions.W015])
|
|
|
|
@override_settings(
|
|
SESSION_COOKIE_HTTPONLY=True,
|
|
INSTALLED_APPS=["django.contrib.sessions"],
|
|
MIDDLEWARE_CLASSES=["django.contrib.sessions.middleware.SessionMiddleware"])
|
|
def test_session_cookie_httponly_true(self):
|
|
"""
|
|
If SESSION_COOKIE_HTTPONLY is on, there's no warning about it.
|
|
"""
|
|
self.assertEqual(self.func(None), [])
|
|
|
|
|
|
class CheckCSRFMiddlewareTest(TestCase):
|
|
@property
|
|
def func(self):
|
|
from django.core.checks.security.csrf import check_csrf_middleware
|
|
return check_csrf_middleware
|
|
|
|
@override_settings(MIDDLEWARE_CLASSES=[])
|
|
def test_no_csrf_middleware(self):
|
|
"""
|
|
Warn if CsrfViewMiddleware isn't in MIDDLEWARE_CLASSES.
|
|
"""
|
|
self.assertEqual(self.func(None), [csrf.W003])
|
|
|
|
@override_settings(
|
|
MIDDLEWARE_CLASSES=["django.middleware.csrf.CsrfViewMiddleware"])
|
|
def test_with_csrf_middleware(self):
|
|
self.assertEqual(self.func(None), [])
|
|
|
|
|
|
class CheckCSRFCookieSecureTest(TestCase):
|
|
@property
|
|
def func(self):
|
|
from django.core.checks.security.csrf import check_csrf_cookie_secure
|
|
return check_csrf_cookie_secure
|
|
|
|
@override_settings(
|
|
MIDDLEWARE_CLASSES=["django.middleware.csrf.CsrfViewMiddleware"],
|
|
CSRF_COOKIE_SECURE=False)
|
|
def test_with_csrf_cookie_secure_false(self):
|
|
"""
|
|
Warn if CsrfViewMiddleware is in MIDDLEWARE_CLASSES but
|
|
CSRF_COOKIE_SECURE isn't True.
|
|
"""
|
|
self.assertEqual(self.func(None), [csrf.W016])
|
|
|
|
@override_settings(MIDDLEWARE_CLASSES=[], CSRF_COOKIE_SECURE=False)
|
|
def test_with_csrf_cookie_secure_false_no_middleware(self):
|
|
"""
|
|
No warning if CsrfViewMiddleware isn't in MIDDLEWARE_CLASSES, even if
|
|
CSRF_COOKIE_SECURE is False.
|
|
"""
|
|
self.assertEqual(self.func(None), [])
|
|
|
|
@override_settings(
|
|
MIDDLEWARE_CLASSES=["django.middleware.csrf.CsrfViewMiddleware"],
|
|
CSRF_COOKIE_SECURE=True)
|
|
def test_with_csrf_cookie_secure_true(self):
|
|
self.assertEqual(self.func(None), [])
|
|
|
|
|
|
class CheckCSRFCookieHttpOnlyTest(TestCase):
|
|
@property
|
|
def func(self):
|
|
from django.core.checks.security.csrf import check_csrf_cookie_httponly
|
|
return check_csrf_cookie_httponly
|
|
|
|
@override_settings(
|
|
MIDDLEWARE_CLASSES=["django.middleware.csrf.CsrfViewMiddleware"],
|
|
CSRF_COOKIE_HTTPONLY=False)
|
|
def test_with_csrf_cookie_httponly_false(self):
|
|
"""
|
|
Warn if CsrfViewMiddleware is in MIDDLEWARE_CLASSES but
|
|
CSRF_COOKIE_HTTPONLY isn't True.
|
|
"""
|
|
self.assertEqual(self.func(None), [csrf.W017])
|
|
|
|
@override_settings(MIDDLEWARE_CLASSES=[], CSRF_COOKIE_HTTPONLY=False)
|
|
def test_with_csrf_cookie_httponly_false_no_middleware(self):
|
|
"""
|
|
No warning if CsrfViewMiddleware isn't in MIDDLEWARE_CLASSES, even if
|
|
CSRF_COOKIE_HTTPONLY is False.
|
|
"""
|
|
self.assertEqual(self.func(None), [])
|
|
|
|
@override_settings(
|
|
MIDDLEWARE_CLASSES=["django.middleware.csrf.CsrfViewMiddleware"],
|
|
CSRF_COOKIE_HTTPONLY=True)
|
|
def test_with_csrf_cookie_httponly_true(self):
|
|
self.assertEqual(self.func(None), [])
|
|
|
|
|
|
class CheckSecurityMiddlewareTest(TestCase):
|
|
@property
|
|
def func(self):
|
|
from django.core.checks.security.base import check_security_middleware
|
|
return check_security_middleware
|
|
|
|
@override_settings(MIDDLEWARE_CLASSES=[])
|
|
def test_no_security_middleware(self):
|
|
"""
|
|
Warn if SecurityMiddleware isn't in MIDDLEWARE_CLASSES.
|
|
"""
|
|
self.assertEqual(self.func(None), [base.W001])
|
|
|
|
@override_settings(
|
|
MIDDLEWARE_CLASSES=["django.middleware.security.SecurityMiddleware"])
|
|
def test_with_security_middleware(self):
|
|
self.assertEqual(self.func(None), [])
|
|
|
|
|
|
class CheckStrictTransportSecurityTest(TestCase):
|
|
@property
|
|
def func(self):
|
|
from django.core.checks.security.base import check_sts
|
|
return check_sts
|
|
|
|
@override_settings(
|
|
MIDDLEWARE_CLASSES=["django.middleware.security.SecurityMiddleware"],
|
|
SECURE_HSTS_SECONDS=0)
|
|
def test_no_sts(self):
|
|
"""
|
|
Warn if SECURE_HSTS_SECONDS isn't > 0.
|
|
"""
|
|
self.assertEqual(self.func(None), [base.W004])
|
|
|
|
@override_settings(
|
|
MIDDLEWARE_CLASSES=[],
|
|
SECURE_HSTS_SECONDS=0)
|
|
def test_no_sts_no_middlware(self):
|
|
"""
|
|
Don't warn if SECURE_HSTS_SECONDS isn't > 0 and SecurityMiddleware isn't
|
|
installed.
|
|
"""
|
|
self.assertEqual(self.func(None), [])
|
|
|
|
@override_settings(
|
|
MIDDLEWARE_CLASSES=["django.middleware.security.SecurityMiddleware"],
|
|
SECURE_HSTS_SECONDS=3600)
|
|
def test_with_sts(self):
|
|
self.assertEqual(self.func(None), [])
|
|
|
|
|
|
class CheckStrictTransportSecuritySubdomainsTest(TestCase):
|
|
@property
|
|
def func(self):
|
|
from django.core.checks.security.base import check_sts_include_subdomains
|
|
return check_sts_include_subdomains
|
|
|
|
@override_settings(
|
|
MIDDLEWARE_CLASSES=["django.middleware.security.SecurityMiddleware"],
|
|
SECURE_HSTS_INCLUDE_SUBDOMAINS=False,
|
|
SECURE_HSTS_SECONDS=3600)
|
|
def test_no_sts_subdomains(self):
|
|
"""
|
|
Warn if SECURE_HSTS_INCLUDE_SUBDOMAINS isn't True.
|
|
"""
|
|
self.assertEqual(self.func(None), [base.W005])
|
|
|
|
@override_settings(
|
|
MIDDLEWARE_CLASSES=[],
|
|
SECURE_HSTS_INCLUDE_SUBDOMAINS=False,
|
|
SECURE_HSTS_SECONDS=3600)
|
|
def test_no_sts_subdomains_no_middlware(self):
|
|
"""
|
|
Don't warn if SecurityMiddleware isn't installed.
|
|
"""
|
|
self.assertEqual(self.func(None), [])
|
|
|
|
@override_settings(
|
|
MIDDLEWARE_CLASSES=["django.middleware.security.SecurityMiddleware"],
|
|
SECURE_SSL_REDIRECT=False,
|
|
SECURE_HSTS_SECONDS=None)
|
|
def test_no_sts_subdomains_no_seconds(self):
|
|
"""
|
|
Don't warn if SECURE_HSTS_SECONDS isn't set.
|
|
"""
|
|
self.assertEqual(self.func(None), [])
|
|
|
|
@override_settings(
|
|
MIDDLEWARE_CLASSES=["django.middleware.security.SecurityMiddleware"],
|
|
SECURE_HSTS_INCLUDE_SUBDOMAINS=True,
|
|
SECURE_HSTS_SECONDS=3600)
|
|
def test_with_sts_subdomains(self):
|
|
self.assertEqual(self.func(None), [])
|
|
|
|
|
|
class CheckXFrameOptionsMiddlewareTest(TestCase):
|
|
@property
|
|
def func(self):
|
|
from django.core.checks.security.base import check_xframe_options_middleware
|
|
return check_xframe_options_middleware
|
|
|
|
@override_settings(MIDDLEWARE_CLASSES=[])
|
|
def test_middleware_not_installed(self):
|
|
"""
|
|
Warn if XFrameOptionsMiddleware isn't in MIDDLEWARE_CLASSES.
|
|
"""
|
|
self.assertEqual(self.func(None), [base.W002])
|
|
|
|
@override_settings(MIDDLEWARE_CLASSES=["django.middleware.clickjacking.XFrameOptionsMiddleware"])
|
|
def test_middleware_installed(self):
|
|
self.assertEqual(self.func(None), [])
|
|
|
|
|
|
class CheckXFrameOptionsDenyTest(TestCase):
|
|
@property
|
|
def func(self):
|
|
from django.core.checks.security.base import check_xframe_deny
|
|
return check_xframe_deny
|
|
|
|
@override_settings(
|
|
MIDDLEWARE_CLASSES=["django.middleware.clickjacking.XFrameOptionsMiddleware"],
|
|
X_FRAME_OPTIONS='SAMEORIGIN',
|
|
)
|
|
def test_x_frame_options_not_deny(self):
|
|
"""
|
|
Warn if XFrameOptionsMiddleware is in MIDDLEWARE_CLASSES but
|
|
X_FRAME_OPTIONS isn't 'DENY'.
|
|
"""
|
|
self.assertEqual(self.func(None), [base.W019])
|
|
|
|
@override_settings(MIDDLEWARE_CLASSES=[], X_FRAME_OPTIONS='SAMEORIGIN')
|
|
def test_middleware_not_installed(self):
|
|
"""
|
|
No error if XFrameOptionsMiddleware isn't in MIDDLEWARE_CLASSES even if
|
|
X_FRAME_OPTIONS isn't 'DENY'.
|
|
"""
|
|
self.assertEqual(self.func(None), [])
|
|
|
|
@override_settings(
|
|
MIDDLEWARE_CLASSES=["django.middleware.clickjacking.XFrameOptionsMiddleware"],
|
|
X_FRAME_OPTIONS='DENY',
|
|
)
|
|
def test_xframe_deny(self):
|
|
self.assertEqual(self.func(None), [])
|
|
|
|
|
|
class CheckContentTypeNosniffTest(TestCase):
|
|
@property
|
|
def func(self):
|
|
from django.core.checks.security.base import check_content_type_nosniff
|
|
return check_content_type_nosniff
|
|
|
|
@override_settings(
|
|
MIDDLEWARE_CLASSES=["django.middleware.security.SecurityMiddleware"],
|
|
SECURE_CONTENT_TYPE_NOSNIFF=False)
|
|
def test_no_content_type_nosniff(self):
|
|
"""
|
|
Warn if SECURE_CONTENT_TYPE_NOSNIFF isn't True.
|
|
"""
|
|
self.assertEqual(self.func(None), [base.W006])
|
|
|
|
@override_settings(
|
|
MIDDLEWARE_CLASSES=[],
|
|
SECURE_CONTENT_TYPE_NOSNIFF=False)
|
|
def test_no_content_type_nosniff_no_middleware(self):
|
|
"""
|
|
Don't warn if SECURE_CONTENT_TYPE_NOSNIFF isn't True and
|
|
SecurityMiddleware isn't in MIDDLEWARE_CLASSES.
|
|
"""
|
|
self.assertEqual(self.func(None), [])
|
|
|
|
@override_settings(
|
|
MIDDLEWARE_CLASSES=["django.middleware.security.SecurityMiddleware"],
|
|
SECURE_CONTENT_TYPE_NOSNIFF=True)
|
|
def test_with_content_type_nosniff(self):
|
|
self.assertEqual(self.func(None), [])
|
|
|
|
|
|
class CheckXssFilterTest(TestCase):
|
|
@property
|
|
def func(self):
|
|
from django.core.checks.security.base import check_xss_filter
|
|
return check_xss_filter
|
|
|
|
@override_settings(
|
|
MIDDLEWARE_CLASSES=["django.middleware.security.SecurityMiddleware"],
|
|
SECURE_BROWSER_XSS_FILTER=False)
|
|
def test_no_xss_filter(self):
|
|
"""
|
|
Warn if SECURE_BROWSER_XSS_FILTER isn't True.
|
|
"""
|
|
self.assertEqual(self.func(None), [base.W007])
|
|
|
|
@override_settings(
|
|
MIDDLEWARE_CLASSES=[],
|
|
SECURE_BROWSER_XSS_FILTER=False)
|
|
def test_no_xss_filter_no_middleware(self):
|
|
"""
|
|
Don't warn if SECURE_BROWSER_XSS_FILTER isn't True and
|
|
SecurityMiddleware isn't in MIDDLEWARE_CLASSES.
|
|
"""
|
|
self.assertEqual(self.func(None), [])
|
|
|
|
@override_settings(
|
|
MIDDLEWARE_CLASSES=["django.middleware.security.SecurityMiddleware"],
|
|
SECURE_BROWSER_XSS_FILTER=True)
|
|
def test_with_xss_filter(self):
|
|
self.assertEqual(self.func(None), [])
|
|
|
|
|
|
class CheckSSLRedirectTest(TestCase):
|
|
@property
|
|
def func(self):
|
|
from django.core.checks.security.base import check_ssl_redirect
|
|
return check_ssl_redirect
|
|
|
|
@override_settings(
|
|
MIDDLEWARE_CLASSES=["django.middleware.security.SecurityMiddleware"],
|
|
SECURE_SSL_REDIRECT=False)
|
|
def test_no_ssl_redirect(self):
|
|
"""
|
|
Warn if SECURE_SSL_REDIRECT isn't True.
|
|
"""
|
|
self.assertEqual(self.func(None), [base.W008])
|
|
|
|
@override_settings(
|
|
MIDDLEWARE_CLASSES=[],
|
|
SECURE_SSL_REDIRECT=False)
|
|
def test_no_ssl_redirect_no_middlware(self):
|
|
"""
|
|
Don't warn if SECURE_SSL_REDIRECT is False and SecurityMiddleware isn't
|
|
installed.
|
|
"""
|
|
self.assertEqual(self.func(None), [])
|
|
|
|
@override_settings(
|
|
MIDDLEWARE_CLASSES=["django.middleware.security.SecurityMiddleware"],
|
|
SECURE_SSL_REDIRECT=True)
|
|
def test_with_ssl_redirect(self):
|
|
self.assertEqual(self.func(None), [])
|
|
|
|
|
|
class CheckSecretKeyTest(TestCase):
|
|
@property
|
|
def func(self):
|
|
from django.core.checks.security.base import check_secret_key
|
|
return check_secret_key
|
|
|
|
@override_settings(SECRET_KEY=('abcdefghijklmnopqrstuvwx' * 2) + 'ab')
|
|
def test_okay_secret_key(self):
|
|
self.assertEqual(len(settings.SECRET_KEY), base.SECRET_KEY_MIN_LENGTH)
|
|
self.assertGreater(len(set(settings.SECRET_KEY)), base.SECRET_KEY_MIN_UNIQUE_CHARACTERS)
|
|
self.assertEqual(self.func(None), [])
|
|
|
|
@override_settings(SECRET_KEY='')
|
|
def test_empty_secret_key(self):
|
|
self.assertEqual(self.func(None), [base.W009])
|
|
|
|
@override_settings(SECRET_KEY=None)
|
|
def test_missing_secret_key(self):
|
|
del settings.SECRET_KEY
|
|
self.assertEqual(self.func(None), [base.W009])
|
|
|
|
@override_settings(SECRET_KEY=None)
|
|
def test_none_secret_key(self):
|
|
self.assertEqual(self.func(None), [base.W009])
|
|
|
|
@override_settings(SECRET_KEY=('abcdefghijklmnopqrstuvwx' * 2) + 'a')
|
|
def test_low_length_secret_key(self):
|
|
self.assertEqual(len(settings.SECRET_KEY), base.SECRET_KEY_MIN_LENGTH - 1)
|
|
self.assertEqual(self.func(None), [base.W009])
|
|
|
|
@override_settings(SECRET_KEY='abcd' * 20)
|
|
def test_low_entropy_secret_key(self):
|
|
self.assertGreater(len(settings.SECRET_KEY), base.SECRET_KEY_MIN_LENGTH)
|
|
self.assertLess(len(set(settings.SECRET_KEY)), base.SECRET_KEY_MIN_UNIQUE_CHARACTERS)
|
|
self.assertEqual(self.func(None), [base.W009])
|
|
|
|
|
|
class CheckDebugTest(TestCase):
|
|
@property
|
|
def func(self):
|
|
from django.core.checks.security.base import check_debug
|
|
return check_debug
|
|
|
|
@override_settings(DEBUG=True)
|
|
def test_debug_true(self):
|
|
"""
|
|
Warn if DEBUG is True.
|
|
"""
|
|
self.assertEqual(self.func(None), [base.W018])
|
|
|
|
@override_settings(DEBUG=False)
|
|
def test_debug_false(self):
|
|
self.assertEqual(self.func(None), [])
|