Sign Up
Log In
Log In
or
Sign Up
Places
All Projects
Status Monitor
Collapse sidebar
Cloud:OpenStack:Newton
python-Django
CVE-2020-13254.patch
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File CVE-2020-13254.patch of Package python-Django
commit dca6df3ed1d10f122917086529a925824ed17023 Author: Dan Palmer <dan@danpalmer.me> Date: Wed May 20 11:45:31 2020 +0200 [2.2.x] Fixed CVE-2020-13254 -- Enforced cache key validation in memcached backends. In cases where a memcached backend does not perform key validation, passing malformed cache keys could result in a key collision, and potential data leakage. In order to avoid this vulnerability, key validation is added to the memcached cache backends. Index: Django-1.8.19/django/core/cache/__init__.py =================================================================== --- Django-1.8.19.orig/django/core/cache/__init__.py +++ Django-1.8.19/django/core/cache/__init__.py @@ -18,7 +18,7 @@ import warnings from django.conf import settings from django.core import signals from django.core.cache.backends.base import ( - InvalidCacheBackendError, CacheKeyWarning, BaseCache) + InvalidCacheBackendError, InvalidCacheKey, CacheKeyWarning, BaseCache) from django.core.exceptions import ImproperlyConfigured from django.utils.deprecation import RemovedInDjango19Warning from django.utils.module_loading import import_string @@ -26,7 +26,7 @@ from django.utils.module_loading import __all__ = [ 'get_cache', 'cache', 'DEFAULT_CACHE_ALIAS', 'InvalidCacheBackendError', - 'CacheKeyWarning', 'BaseCache', + 'CacheKeyWarning', 'BaseCache', 'InvalidCacheKey', ] DEFAULT_CACHE_ALIAS = 'default' Index: Django-1.8.19/django/core/cache/backends/base.py =================================================================== --- Django-1.8.19.orig/django/core/cache/backends/base.py +++ Django-1.8.19/django/core/cache/backends/base.py @@ -16,6 +16,10 @@ class CacheKeyWarning(DjangoRuntimeWarni pass +class InvalidCacheKey(ValueError): + pass + + # Stub class to ensure not passing in a `timeout` argument results in # the default timeout DEFAULT_TIMEOUT = object() @@ -213,15 +217,9 @@ class BaseCache(object): cache code. """ - if len(key) > MEMCACHE_MAX_KEY_LENGTH: - warnings.warn('Cache key will cause errors if used with memcached: ' - '%s (longer than %s)' % (key, MEMCACHE_MAX_KEY_LENGTH), - CacheKeyWarning) - for char in key: - if ord(char) < 33 or ord(char) == 127: - warnings.warn('Cache key contains characters that will cause ' - 'errors if used with memcached: %r' % key, - CacheKeyWarning) + for warning in memcache_key_warnings(key): + warnings.warn(warning, CacheKeyWarning) + def incr_version(self, key, delta=1, version=None): """Adds delta to the cache version for the supplied key. Returns the @@ -247,3 +245,18 @@ class BaseCache(object): def close(self, **kwargs): """Close the cache connection""" pass + + +def memcache_key_warnings(key): + if len(key) > MEMCACHE_MAX_KEY_LENGTH: + yield ( + 'Cache key will cause errors if used with memcached: %r ' + '(longer than %s)' % (key, MEMCACHE_MAX_KEY_LENGTH) + ) + for char in key: + if ord(char) < 33 or ord(char) == 127: + yield ( + 'Cache key contains characters that will cause errors if ' + 'used with memcached: %r' % key, CacheKeyWarning + ) + break Index: Django-1.8.19/django/core/cache/backends/memcached.py =================================================================== --- Django-1.8.19.orig/django/core/cache/backends/memcached.py +++ Django-1.8.19/django/core/cache/backends/memcached.py @@ -3,7 +3,9 @@ import pickle import time -from django.core.cache.backends.base import DEFAULT_TIMEOUT, BaseCache +from django.core.cache.backends.base import ( + DEFAULT_TIMEOUT, BaseCache, InvalidCacheKey, memcache_key_warnings, +) from django.utils import six from django.utils.deprecation import ( RemovedInDjango19Warning, RenameMethodsBase, @@ -77,10 +79,12 @@ class BaseMemcachedCache(six.with_metacl def add(self, key, value, timeout=DEFAULT_TIMEOUT, version=None): key = self.make_key(key, version=version) + self.validate_key(key) return self._cache.add(key, value, self.get_backend_timeout(timeout)) def get(self, key, default=None, version=None): key = self.make_key(key, version=version) + self.validate_key(key) val = self._cache.get(key) if val is None: return default @@ -88,16 +92,20 @@ class BaseMemcachedCache(six.with_metacl def set(self, key, value, timeout=DEFAULT_TIMEOUT, version=None): key = self.make_key(key, version=version) + self.validate_key(key) if not self._cache.set(key, value, self.get_backend_timeout(timeout)): # make sure the key doesn't keep its old value in case of failure to set (memcached's 1MB limit) self._cache.delete(key) def delete(self, key, version=None): key = self.make_key(key, version=version) + self.validate_key(key) self._cache.delete(key) def get_many(self, keys, version=None): new_keys = [self.make_key(x, version=version) for x in keys] + for key in new_keys: + self.validate_key(key) ret = self._cache.get_multi(new_keys) if ret: _ = {} @@ -112,6 +120,7 @@ class BaseMemcachedCache(six.with_metacl def incr(self, key, delta=1, version=None): key = self.make_key(key, version=version) + self.validate_key(key) # memcached doesn't support a negative delta if delta < 0: return self._cache.decr(key, -delta) @@ -130,6 +139,7 @@ class BaseMemcachedCache(six.with_metacl def decr(self, key, delta=1, version=None): key = self.make_key(key, version=version) + self.validate_key(key) # memcached doesn't support a negative delta if delta < 0: return self._cache.incr(key, -delta) @@ -150,6 +160,7 @@ class BaseMemcachedCache(six.with_metacl safe_data = {} for key, value in data.items(): key = self.make_key(key, version=version) + self.validate_key(key) safe_data[key] = value self._cache.set_multi(safe_data, self.get_backend_timeout(timeout)) @@ -160,6 +171,10 @@ class BaseMemcachedCache(six.with_metacl def clear(self): self._cache.flush_all() + def validate_key(self, key): + for warning in memcache_key_warnings(key): + raise InvalidCacheKey(warning) + class MemcachedCache(BaseMemcachedCache): "An implementation of a cache binding using python-memcached" Index: Django-1.8.19/tests/cache/tests.py =================================================================== --- Django-1.8.19.orig/tests/cache/tests.py +++ Django-1.8.19/tests/cache/tests.py @@ -18,7 +18,7 @@ from django.conf import settings from django.core import management, signals from django.core.cache import ( DEFAULT_CACHE_ALIAS, CacheKeyWarning, InvalidCacheBackendError, cache, - caches, close_caches, get_cache, + InvalidCacheKey, caches, close_caches, get_cache, ) from django.core.cache.utils import make_template_fragment_key from django.db import connection, connections, transaction @@ -546,11 +546,10 @@ class BaseCacheTests(object): def test_invalid_keys(self): """ - All the builtin backends (except memcached, see below) should warn on - keys that would be refused by memcached. This encourages portable - caching code without making it too difficult to use production backends - with more liberal key rules. Refs #6447. - + All the builtin backends should warn (except memcached that should + error) on keys that would be refused by memcached. This encourages + portable caching code without making it too difficult to use production + backends with more liberal key rules. Refs #6447. """ # mimic custom ``make_key`` method being defined since the default will # never show the below warnings @@ -1101,11 +1100,14 @@ class MemcachedCacheTests(BaseCacheTests In order to be memcached-API-library agnostic, we only assert that a generic exception of some kind is raised. + Whilst other backends merely warn, memcached should raise for an + invalid key. """ # memcached does not allow whitespace or control characters in keys self.assertRaises(Exception, cache.set, 'key with spaces', 'value') # memcached limits key length to 250 self.assertRaises(Exception, cache.set, 'a' * 251, 'value') + self.assertRaises(Exception, cache.set, 'a' * 250 + '清', 'value') # Explicitly display a skipped test if no configured cache uses MemcachedCache @unittest.skipUnless(
Locations
Projects
Search
Status Monitor
Help
OpenBuildService.org
Documentation
API Documentation
Code of Conduct
Contact
Support
@OBShq
Terms
openSUSE Build Service is sponsored by
The Open Build Service is an
openSUSE project
.
Sign Up
Log In
Places
Places
All Projects
Status Monitor