Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit d63e209

Browse files
aaugustintimgraham
authored andcommitted
[1.6.x] Prevented leaking the CSRF token through caching.
This is a security fix. Disclosure will follow shortly. Backport of c083e38 from master
1 parent 4352a50 commit d63e209

File tree

2 files changed

+37
-1
lines changed

2 files changed

+37
-1
lines changed

django/middleware/cache.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,8 @@
4747

4848
from django.conf import settings
4949
from django.core.cache import get_cache, DEFAULT_CACHE_ALIAS
50-
from django.utils.cache import get_cache_key, learn_cache_key, patch_response_headers, get_max_age
50+
from django.utils.cache import (get_cache_key, get_max_age, has_vary_header,
51+
learn_cache_key, patch_response_headers)
5152

5253

5354
class UpdateCacheMiddleware(object):
@@ -90,8 +91,15 @@ def process_response(self, request, response):
9091
if not self._should_update_cache(request, response):
9192
# We don't need to update the cache, just return.
9293
return response
94+
9395
if response.streaming or response.status_code != 200:
9496
return response
97+
98+
# Don't cache responses that set a user-specific (and maybe security
99+
# sensitive) cookie in response to a cookie-less request.
100+
if not request.COOKIES and response.cookies and has_vary_header(response, 'Cookie'):
101+
return response
102+
95103
# Try to get the timeout from the "max-age" section of the "Cache-
96104
# Control" header before reverting to using the default cache_timeout
97105
# length.

tests/cache/tests.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,14 @@
1919
from django.core.cache import get_cache
2020
from django.core.cache.backends.base import (CacheKeyWarning,
2121
InvalidCacheBackendError)
22+
from django.core.context_processors import csrf
2223
from django.db import router, transaction
2324
from django.core.cache.utils import make_template_fragment_key
2425
from django.http import (HttpResponse, HttpRequest, StreamingHttpResponse,
2526
QueryDict)
2627
from django.middleware.cache import (FetchFromCacheMiddleware,
2728
UpdateCacheMiddleware, CacheMiddleware)
29+
from django.middleware.csrf import CsrfViewMiddleware
2830
from django.template import Template
2931
from django.template.response import TemplateResponse
3032
from django.test import TestCase, TransactionTestCase, RequestFactory
@@ -1578,6 +1580,10 @@ def hello_world_view(request, value):
15781580
return HttpResponse('Hello World %s' % value)
15791581

15801582

1583+
def csrf_view(request):
1584+
return HttpResponse(csrf(request)['csrf_token'])
1585+
1586+
15811587
@override_settings(
15821588
CACHE_MIDDLEWARE_ALIAS='other',
15831589
CACHE_MIDDLEWARE_KEY_PREFIX='middlewareprefix',
@@ -1797,6 +1803,28 @@ def test_view_decorator(self):
17971803
response = other_with_prefix_view(request, '16')
17981804
self.assertEqual(response.content, b'Hello World 16')
17991805

1806+
def test_sensitive_cookie_not_cached(self):
1807+
"""
1808+
Django must prevent caching of responses that set a user-specific (and
1809+
maybe security sensitive) cookie in response to a cookie-less request.
1810+
"""
1811+
csrf_middleware = CsrfViewMiddleware()
1812+
cache_middleware = CacheMiddleware()
1813+
1814+
request = self.factory.get('/view/')
1815+
self.assertIsNone(cache_middleware.process_request(request))
1816+
1817+
csrf_middleware.process_view(request, csrf_view, (), {})
1818+
1819+
response = csrf_view(request)
1820+
1821+
response = csrf_middleware.process_response(request, response)
1822+
response = cache_middleware.process_response(request, response)
1823+
1824+
# Inserting a CSRF cookie in a cookie-less request prevented caching.
1825+
self.assertIsNone(cache_middleware.process_request(request))
1826+
1827+
18001828
@override_settings(
18011829
CACHE_MIDDLEWARE_KEY_PREFIX='settingsprefix',
18021830
CACHE_MIDDLEWARE_SECONDS=1,

0 commit comments

Comments
 (0)