mirror of https://github.com/django/django.git
[1.6.x] Prevented leaking the CSRF token through caching.
This is a security fix. Disclosure will follow shortly.
Backport of c083e3815a
from master
This commit is contained in:
parent
4352a50871
commit
d63e20942f
|
@ -47,7 +47,8 @@ import warnings
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.core.cache import get_cache, DEFAULT_CACHE_ALIAS
|
from django.core.cache import get_cache, 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):
|
class UpdateCacheMiddleware(object):
|
||||||
|
@ -90,8 +91,15 @@ class UpdateCacheMiddleware(object):
|
||||||
if not self._should_update_cache(request, response):
|
if not self._should_update_cache(request, response):
|
||||||
# We don't need to update the cache, just return.
|
# We don't need to update the cache, just return.
|
||||||
return response
|
return response
|
||||||
|
|
||||||
if response.streaming or response.status_code != 200:
|
if response.streaming or response.status_code != 200:
|
||||||
return response
|
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-
|
# Try to get the timeout from the "max-age" section of the "Cache-
|
||||||
# Control" header before reverting to using the default cache_timeout
|
# Control" header before reverting to using the default cache_timeout
|
||||||
# length.
|
# length.
|
||||||
|
|
|
@ -19,12 +19,14 @@ from django.core import management
|
||||||
from django.core.cache import get_cache
|
from django.core.cache import get_cache
|
||||||
from django.core.cache.backends.base import (CacheKeyWarning,
|
from django.core.cache.backends.base import (CacheKeyWarning,
|
||||||
InvalidCacheBackendError)
|
InvalidCacheBackendError)
|
||||||
|
from django.core.context_processors import csrf
|
||||||
from django.db import router, transaction
|
from django.db import router, transaction
|
||||||
from django.core.cache.utils import make_template_fragment_key
|
from django.core.cache.utils import make_template_fragment_key
|
||||||
from django.http import (HttpResponse, HttpRequest, StreamingHttpResponse,
|
from django.http import (HttpResponse, HttpRequest, StreamingHttpResponse,
|
||||||
QueryDict)
|
QueryDict)
|
||||||
from django.middleware.cache import (FetchFromCacheMiddleware,
|
from django.middleware.cache import (FetchFromCacheMiddleware,
|
||||||
UpdateCacheMiddleware, CacheMiddleware)
|
UpdateCacheMiddleware, CacheMiddleware)
|
||||||
|
from django.middleware.csrf import CsrfViewMiddleware
|
||||||
from django.template import Template
|
from django.template import Template
|
||||||
from django.template.response import TemplateResponse
|
from django.template.response import TemplateResponse
|
||||||
from django.test import TestCase, TransactionTestCase, RequestFactory
|
from django.test import TestCase, TransactionTestCase, RequestFactory
|
||||||
|
@ -1578,6 +1580,10 @@ def hello_world_view(request, value):
|
||||||
return HttpResponse('Hello World %s' % value)
|
return HttpResponse('Hello World %s' % value)
|
||||||
|
|
||||||
|
|
||||||
|
def csrf_view(request):
|
||||||
|
return HttpResponse(csrf(request)['csrf_token'])
|
||||||
|
|
||||||
|
|
||||||
@override_settings(
|
@override_settings(
|
||||||
CACHE_MIDDLEWARE_ALIAS='other',
|
CACHE_MIDDLEWARE_ALIAS='other',
|
||||||
CACHE_MIDDLEWARE_KEY_PREFIX='middlewareprefix',
|
CACHE_MIDDLEWARE_KEY_PREFIX='middlewareprefix',
|
||||||
|
@ -1797,6 +1803,28 @@ class CacheMiddlewareTest(IgnorePendingDeprecationWarningsMixin, TestCase):
|
||||||
response = other_with_prefix_view(request, '16')
|
response = other_with_prefix_view(request, '16')
|
||||||
self.assertEqual(response.content, b'Hello World 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(
|
@override_settings(
|
||||||
CACHE_MIDDLEWARE_KEY_PREFIX='settingsprefix',
|
CACHE_MIDDLEWARE_KEY_PREFIX='settingsprefix',
|
||||||
CACHE_MIDDLEWARE_SECONDS=1,
|
CACHE_MIDDLEWARE_SECONDS=1,
|
||||||
|
|
Loading…
Reference in New Issue