浏览代码

Prevented leaking the CSRF token through caching.

This is a security fix. Disclosure will follow shortly.
Aymeric Augustin 11 年之前
父节点
当前提交
c083e3815a
共有 2 个文件被更改,包括 36 次插入1 次删除
  1. 9 1
      django/middleware/cache.py
  2. 27 0
      tests/cache/tests.py

+ 9 - 1
django/middleware/cache.py

@@ -45,7 +45,8 @@ More details about how the caching works:
 
 from django.conf import settings
 from django.core.cache import caches, DEFAULT_CACHE_ALIAS
-from django.utils.cache import get_cache_key, learn_cache_key, patch_response_headers, get_max_age
+from django.utils.cache import (get_cache_key, get_max_age, has_vary_header,
+    learn_cache_key, patch_response_headers)
 
 
 class UpdateCacheMiddleware(object):
@@ -77,8 +78,15 @@ class UpdateCacheMiddleware(object):
         if not self._should_update_cache(request, response):
             # We don't need to update the cache, just return.
             return response
+
         if response.streaming or response.status_code != 200:
             return response
+
+        # Don't cache responses that set a user-specific (and maybe security
+        # sensitive) cookie in response to a cookie-less request.
+        if not request.COOKIES and response.cookies and has_vary_header(response, 'Cookie'):
+            return response
+
         # Try to get the timeout from the "max-age" section of the "Cache-
         # Control" header before reverting to using the default cache_timeout
         # length.

+ 27 - 0
tests/cache/tests.py

@@ -18,11 +18,13 @@ from django.conf import settings
 from django.core import management
 from django.core.cache import (cache, caches, CacheKeyWarning,
     InvalidCacheBackendError, DEFAULT_CACHE_ALIAS)
+from django.core.context_processors import csrf
 from django.db import connection, connections, router, transaction
 from django.core.cache.utils import make_template_fragment_key
 from django.http import HttpResponse, StreamingHttpResponse
 from django.middleware.cache import (FetchFromCacheMiddleware,
     UpdateCacheMiddleware, CacheMiddleware)
+from django.middleware.csrf import CsrfViewMiddleware
 from django.template import Template
 from django.template.response import TemplateResponse
 from django.test import TestCase, TransactionTestCase, RequestFactory, override_settings
@@ -1741,6 +1743,10 @@ def hello_world_view(request, value):
     return HttpResponse('Hello World %s' % value)
 
 
+def csrf_view(request):
+    return HttpResponse(csrf(request)['csrf_token'])
+
+
 @override_settings(
     CACHE_MIDDLEWARE_ALIAS='other',
     CACHE_MIDDLEWARE_KEY_PREFIX='middlewareprefix',
@@ -1905,6 +1911,27 @@ class CacheMiddlewareTest(TestCase):
         response = other_with_prefix_view(request, '16')
         self.assertEqual(response.content, b'Hello World 16')
 
+    def test_sensitive_cookie_not_cached(self):
+        """
+        Django must prevent caching of responses that set a user-specific (and
+        maybe security sensitive) cookie in response to a cookie-less request.
+        """
+        csrf_middleware = CsrfViewMiddleware()
+        cache_middleware = CacheMiddleware()
+
+        request = self.factory.get('/view/')
+        self.assertIsNone(cache_middleware.process_request(request))
+
+        csrf_middleware.process_view(request, csrf_view, (), {})
+
+        response = csrf_view(request)
+
+        response = csrf_middleware.process_response(request, response)
+        response = cache_middleware.process_response(request, response)
+
+        # Inserting a CSRF cookie in a cookie-less request prevented caching.
+        self.assertIsNone(cache_middleware.process_request(request))
+
 
 @override_settings(
     CACHE_MIDDLEWARE_KEY_PREFIX='settingsprefix',