Browse Source

Fixed #26628 -- Changed CSRF logger to django.security.csrf.

Holly Becker 8 years ago
parent
commit
55fec16aaf
5 changed files with 49 additions and 21 deletions
  1. 1 1
      django/middleware/csrf.py
  2. 9 2
      docs/ref/csrf.txt
  3. 3 0
      docs/releases/1.11.txt
  4. 11 4
      docs/topics/logging.txt
  5. 25 14
      tests/csrf_tests/tests.py

+ 1 - 1
django/middleware/csrf.py

@@ -20,7 +20,7 @@ from django.utils.http import is_same_domain
 from django.utils.six.moves import zip
 from django.utils.six.moves import zip
 from django.utils.six.moves.urllib.parse import urlparse
 from django.utils.six.moves.urllib.parse import urlparse
 
 
-logger = logging.getLogger('django.request')
+logger = logging.getLogger('django.security.csrf')
 
 
 REASON_NO_REFERER = "Referer checking failed - no Referer."
 REASON_NO_REFERER = "Referer checking failed - no Referer."
 REASON_BAD_REFERER = "Referer checking failed - %s does not match any trusted origins."
 REASON_BAD_REFERER = "Referer checking failed - %s does not match any trusted origins."

+ 9 - 2
docs/ref/csrf.txt

@@ -192,6 +192,8 @@ both is fine, and will incur minimal overhead.
     If you are using class-based views, you can refer to
     If you are using class-based views, you can refer to
     :ref:`Decorating class-based views<decorating-class-based-views>`.
     :ref:`Decorating class-based views<decorating-class-based-views>`.
 
 
+.. _csrf-rejected-requests:
+
 Rejected requests
 Rejected requests
 =================
 =================
 
 
@@ -205,8 +207,13 @@ The error page, however, is not very friendly, so you may want to provide your
 own view for handling this condition.  To do this, simply set the
 own view for handling this condition.  To do this, simply set the
 :setting:`CSRF_FAILURE_VIEW` setting.
 :setting:`CSRF_FAILURE_VIEW` setting.
 
 
-CSRF failures are logged as warnings to the :ref:`django-request-logger`
-logger.
+CSRF failures are logged as warnings to the :ref:`django.security.csrf
+<django-security-logger>` logger.
+
+.. versionchanged:: 1.11
+
+    In older versions, CSRF failures are logged to the ``django.request``
+    logger.
 
 
 .. _how-csrf-works:
 .. _how-csrf-works:
 
 

+ 3 - 0
docs/releases/1.11.txt

@@ -246,6 +246,9 @@ Miscellaneous
 
 
 * Support for SpatiaLite < 4.0 is dropped.
 * Support for SpatiaLite < 4.0 is dropped.
 
 
+* CSRF failures are logged to the ``django.security.csrf ``` logger instead of
+  ``django.request``.
+
 .. _deprecated-features-1.11:
 .. _deprecated-features-1.11:
 
 
 Features deprecated in 1.11
 Features deprecated in 1.11

+ 11 - 4
docs/topics/logging.txt

@@ -532,20 +532,23 @@ This logging does not include framework-level initialization (e.g.
 ``COMMIT``, and ``ROLLBACK``). Turn on query logging in your database if you
 ``COMMIT``, and ``ROLLBACK``). Turn on query logging in your database if you
 wish to view all database queries.
 wish to view all database queries.
 
 
+.. _django-security-logger:
+
 ``django.security.*``
 ``django.security.*``
 ~~~~~~~~~~~~~~~~~~~~~~
 ~~~~~~~~~~~~~~~~~~~~~~
 
 
 The security loggers will receive messages on any occurrence of
 The security loggers will receive messages on any occurrence of
-:exc:`~django.core.exceptions.SuspiciousOperation`. There is a sub-logger for
-each sub-type of SuspiciousOperation. The level of the log event depends on
-where the exception is handled.  Most occurrences are logged as a warning, while
+:exc:`~django.core.exceptions.SuspiciousOperation` and other security-related
+errors. There is a sub-logger for each subtype of security error, including all
+``SuspiciousOperation``\s. The level of the log event depends on where the
+exception is handled.  Most occurrences are logged as a warning, while
 any ``SuspiciousOperation`` that reaches the WSGI handler will be logged as an
 any ``SuspiciousOperation`` that reaches the WSGI handler will be logged as an
 error. For example, when an HTTP ``Host`` header is included in a request from
 error. For example, when an HTTP ``Host`` header is included in a request from
 a client that does not match :setting:`ALLOWED_HOSTS`, Django will return a 400
 a client that does not match :setting:`ALLOWED_HOSTS`, Django will return a 400
 response, and an error message will be logged to the
 response, and an error message will be logged to the
 ``django.security.DisallowedHost`` logger.
 ``django.security.DisallowedHost`` logger.
 
 
-These log events will reach the 'django' logger by default, which mails error
+These log events will reach the ``django`` logger by default, which mails error
 events to admins when ``DEBUG=False``. Requests resulting in a 400 response due
 events to admins when ``DEBUG=False``. Requests resulting in a 400 response due
 to a ``SuspiciousOperation`` will not be logged to the ``django.request``
 to a ``SuspiciousOperation`` will not be logged to the ``django.request``
 logger, but only to the ``django.security`` logger.
 logger, but only to the ``django.security`` logger.
@@ -567,6 +570,10 @@ specific logger following this example:
         },
         },
     },
     },
 
 
+Other ``django.security`` loggers not based on ``SuspiciousOperation`` are:
+
+* ``django.security.csrf``: For :ref:`CSRF failures <csrf-rejected-requests>`.
+
 ``django.db.backends.schema``
 ``django.db.backends.schema``
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 

+ 25 - 14
tests/csrf_tests/tests.py

@@ -8,12 +8,13 @@ import warnings
 from django.conf import settings
 from django.conf import settings
 from django.http import HttpRequest, HttpResponse
 from django.http import HttpRequest, HttpResponse
 from django.middleware.csrf import (
 from django.middleware.csrf import (
-    CSRF_TOKEN_LENGTH, CsrfViewMiddleware,
-    _compare_salted_tokens as equivalent_tokens, get_token,
+    CSRF_TOKEN_LENGTH, REASON_BAD_TOKEN, REASON_NO_CSRF_COOKIE,
+    CsrfViewMiddleware, _compare_salted_tokens as equivalent_tokens, get_token,
 )
 )
 from django.template import RequestContext, Template
 from django.template import RequestContext, Template
 from django.template.context_processors import csrf
 from django.template.context_processors import csrf
 from django.test import SimpleTestCase, override_settings
 from django.test import SimpleTestCase, override_settings
+from django.test.utils import patch_logger
 from django.utils.encoding import force_bytes
 from django.utils.encoding import force_bytes
 from django.utils.six import text_type
 from django.utils.six import text_type
 from django.views.decorators.csrf import (
 from django.views.decorators.csrf import (
@@ -203,18 +204,22 @@ class CsrfViewMiddlewareTest(SimpleTestCase):
         Check that if no CSRF cookies is present, the middleware rejects the
         Check that if no CSRF cookies is present, the middleware rejects the
         incoming request.  This will stop login CSRF.
         incoming request.  This will stop login CSRF.
         """
         """
-        req = self._get_POST_no_csrf_cookie_request()
-        req2 = CsrfViewMiddleware().process_view(req, post_form_view, (), {})
-        self.assertEqual(403, req2.status_code)
+        with patch_logger('django.security.csrf', 'warning') as logger_calls:
+            req = self._get_POST_no_csrf_cookie_request()
+            req2 = CsrfViewMiddleware().process_view(req, post_form_view, (), {})
+            self.assertEqual(403, req2.status_code)
+            self.assertEqual(logger_calls[0], 'Forbidden (%s): ' % REASON_NO_CSRF_COOKIE)
 
 
     def test_process_request_csrf_cookie_no_token(self):
     def test_process_request_csrf_cookie_no_token(self):
         """
         """
         Check that if a CSRF cookie is present but no token, the middleware
         Check that if a CSRF cookie is present but no token, the middleware
         rejects the incoming request.
         rejects the incoming request.
         """
         """
-        req = self._get_POST_csrf_cookie_request()
-        req2 = CsrfViewMiddleware().process_view(req, post_form_view, (), {})
-        self.assertEqual(403, req2.status_code)
+        with patch_logger('django.security.csrf', 'warning') as logger_calls:
+            req = self._get_POST_csrf_cookie_request()
+            req2 = CsrfViewMiddleware().process_view(req, post_form_view, (), {})
+            self.assertEqual(403, req2.status_code)
+            self.assertEqual(logger_calls[0], 'Forbidden (%s): ' % REASON_BAD_TOKEN)
 
 
     def test_process_request_csrf_cookie_and_token(self):
     def test_process_request_csrf_cookie_and_token(self):
         """
         """
@@ -258,13 +263,17 @@ class CsrfViewMiddlewareTest(SimpleTestCase):
         """
         """
         req = TestingHttpRequest()
         req = TestingHttpRequest()
         req.method = 'PUT'
         req.method = 'PUT'
-        req2 = CsrfViewMiddleware().process_view(req, post_form_view, (), {})
-        self.assertEqual(403, req2.status_code)
+        with patch_logger('django.security.csrf', 'warning') as logger_calls:
+            req2 = CsrfViewMiddleware().process_view(req, post_form_view, (), {})
+            self.assertEqual(403, req2.status_code)
+            self.assertEqual(logger_calls[0], 'Forbidden (%s): ' % REASON_NO_CSRF_COOKIE)
 
 
         req = TestingHttpRequest()
         req = TestingHttpRequest()
         req.method = 'DELETE'
         req.method = 'DELETE'
-        req2 = CsrfViewMiddleware().process_view(req, post_form_view, (), {})
-        self.assertEqual(403, req2.status_code)
+        with patch_logger('django.security.csrf', 'warning') as logger_calls:
+            req2 = CsrfViewMiddleware().process_view(req, post_form_view, (), {})
+            self.assertEqual(403, req2.status_code)
+            self.assertEqual(logger_calls[0], 'Forbidden (%s): ' % REASON_NO_CSRF_COOKIE)
 
 
     def test_put_and_delete_allowed(self):
     def test_put_and_delete_allowed(self):
         """
         """
@@ -681,5 +690,7 @@ class CsrfViewMiddlewareTest(SimpleTestCase):
         self.assertIsNone(resp)
         self.assertIsNone(resp)
 
 
         req = CsrfPostRequest(token, raise_error=True)
         req = CsrfPostRequest(token, raise_error=True)
-        resp = CsrfViewMiddleware().process_view(req, post_form_view, (), {})
-        self.assertEqual(resp.status_code, 403)
+        with patch_logger('django.security.csrf', 'warning') as logger_calls:
+            resp = CsrfViewMiddleware().process_view(req, post_form_view, (), {})
+            self.assertEqual(resp.status_code, 403)
+            self.assertEqual(logger_calls[0], 'Forbidden (%s): ' % REASON_BAD_TOKEN)