Refs #31274 -- Removed support for the pre-Django 3.1 encoding format of sessions.
Per deprecation timeline.
This commit is contained in:
parent
66b4046d68
commit
8250145a0c
|
@ -1,16 +1,11 @@
|
|||
import base64
|
||||
import logging
|
||||
import string
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
from django.conf import settings
|
||||
from django.contrib.sessions.exceptions import SuspiciousSession
|
||||
from django.core import signing
|
||||
from django.core.exceptions import SuspiciousOperation
|
||||
from django.utils import timezone
|
||||
from django.utils.crypto import (
|
||||
constant_time_compare, get_random_string, salted_hmac,
|
||||
)
|
||||
from django.utils.crypto import get_random_string
|
||||
from django.utils.module_loading import import_string
|
||||
|
||||
# session_key should not be case sensitive because some backends can store it
|
||||
|
@ -91,16 +86,8 @@ class SessionBase:
|
|||
def delete_test_cookie(self):
|
||||
del self[self.TEST_COOKIE_NAME]
|
||||
|
||||
def _hash(self, value):
|
||||
# RemovedInDjango40Warning: pre-Django 3.1 format will be invalid.
|
||||
key_salt = "django.contrib.sessions" + self.__class__.__name__
|
||||
return salted_hmac(key_salt, value).hexdigest()
|
||||
|
||||
def encode(self, session_dict):
|
||||
"Return the given session dictionary serialized and encoded as a string."
|
||||
# RemovedInDjango40Warning: DEFAULT_HASHING_ALGORITHM will be removed.
|
||||
if settings.DEFAULT_HASHING_ALGORITHM == 'sha1':
|
||||
return self._legacy_encode(session_dict)
|
||||
return signing.dumps(
|
||||
session_dict, salt=self.key_salt, serializer=self.serializer,
|
||||
compress=True,
|
||||
|
@ -109,44 +96,14 @@ class SessionBase:
|
|||
def decode(self, session_data):
|
||||
try:
|
||||
return signing.loads(session_data, salt=self.key_salt, serializer=self.serializer)
|
||||
# RemovedInDjango40Warning: when the deprecation ends, handle here
|
||||
# exceptions similar to what _legacy_decode() does now.
|
||||
except signing.BadSignature:
|
||||
try:
|
||||
# Return an empty session if data is not in the pre-Django 3.1
|
||||
# format.
|
||||
return self._legacy_decode(session_data)
|
||||
except Exception:
|
||||
logger = logging.getLogger('django.security.SuspiciousSession')
|
||||
logger.warning('Session data corrupted')
|
||||
return {}
|
||||
logger = logging.getLogger('django.security.SuspiciousSession')
|
||||
logger.warning('Session data corrupted')
|
||||
except Exception:
|
||||
return self._legacy_decode(session_data)
|
||||
|
||||
def _legacy_encode(self, session_dict):
|
||||
# RemovedInDjango40Warning.
|
||||
serialized = self.serializer().dumps(session_dict)
|
||||
hash = self._hash(serialized)
|
||||
return base64.b64encode(hash.encode() + b':' + serialized).decode('ascii')
|
||||
|
||||
def _legacy_decode(self, session_data):
|
||||
# RemovedInDjango40Warning: pre-Django 3.1 format will be invalid.
|
||||
encoded_data = base64.b64decode(session_data.encode('ascii'))
|
||||
try:
|
||||
# could produce ValueError if there is no ':'
|
||||
hash, serialized = encoded_data.split(b':', 1)
|
||||
expected_hash = self._hash(serialized)
|
||||
if not constant_time_compare(hash.decode(), expected_hash):
|
||||
raise SuspiciousSession("Session data corrupted")
|
||||
else:
|
||||
return self.serializer().loads(serialized)
|
||||
except Exception as e:
|
||||
# ValueError, SuspiciousOperation, unpickling exceptions. If any of
|
||||
# these happen, just return an empty dictionary (an empty session).
|
||||
if isinstance(e, SuspiciousOperation):
|
||||
logger = logging.getLogger('django.security.%s' % e.__class__.__name__)
|
||||
logger.warning(str(e))
|
||||
return {}
|
||||
# ValueError, unpickling exceptions. If any of these happen, just
|
||||
# return an empty dictionary (an empty session).
|
||||
pass
|
||||
return {}
|
||||
|
||||
def update(self, dict_):
|
||||
self._session.update(dict_)
|
||||
|
|
|
@ -283,3 +283,5 @@ to remove usage of these features.
|
|||
|
||||
* Support for the pre-Django 3.1 password reset tokens in the admin site (that
|
||||
use the SHA-1 hashing algorithm) is removed.
|
||||
|
||||
* Support for the pre-Django 3.1 encoding format of sessions is removed.
|
||||
|
|
|
@ -31,13 +31,13 @@ from django.core import management
|
|||
from django.core.cache import caches
|
||||
from django.core.cache.backends.base import InvalidCacheBackendError
|
||||
from django.core.exceptions import ImproperlyConfigured
|
||||
from django.core.signing import TimestampSigner
|
||||
from django.http import HttpResponse
|
||||
from django.test import (
|
||||
RequestFactory, SimpleTestCase, TestCase, ignore_warnings,
|
||||
override_settings,
|
||||
)
|
||||
from django.utils import timezone
|
||||
from django.utils.deprecation import RemovedInDjango40Warning
|
||||
|
||||
from .models import SessionStore as CustomDatabaseSession
|
||||
|
||||
|
@ -315,25 +315,6 @@ class SessionTestsMixin:
|
|||
encoded = self.session.encode(data)
|
||||
self.assertEqual(self.session.decode(encoded), data)
|
||||
|
||||
@override_settings(SECRET_KEY='django_tests_secret_key')
|
||||
def test_decode_legacy(self):
|
||||
# RemovedInDjango40Warning: pre-Django 3.1 sessions will be invalid.
|
||||
legacy_encoded = (
|
||||
'OWUzNTNmNWQxNTBjOWExZmM4MmQ3NzNhMDRmMjU4NmYwNDUyNGI2NDp7ImEgdGVzd'
|
||||
'CBrZXkiOiJhIHRlc3QgdmFsdWUifQ=='
|
||||
)
|
||||
self.assertEqual(
|
||||
self.session.decode(legacy_encoded),
|
||||
{'a test key': 'a test value'},
|
||||
)
|
||||
|
||||
@ignore_warnings(category=RemovedInDjango40Warning)
|
||||
def test_default_hashing_algorith_legacy_decode(self):
|
||||
with self.settings(DEFAULT_HASHING_ALGORITHM='sha1'):
|
||||
data = {'a test key': 'a test value'}
|
||||
encoded = self.session.encode(data)
|
||||
self.assertEqual(self.session._legacy_decode(encoded), data)
|
||||
|
||||
def test_decode_failure_logged_to_security(self):
|
||||
tests = [
|
||||
base64.b64encode(b'flaskdj:alkdjf').decode('ascii'),
|
||||
|
@ -346,6 +327,11 @@ class SessionTestsMixin:
|
|||
# The failed decode is logged.
|
||||
self.assertIn('Session data corrupted', cm.output[0])
|
||||
|
||||
def test_decode_serializer_exception(self):
|
||||
signer = TimestampSigner(salt=self.session.key_salt)
|
||||
encoded = signer.sign(b'invalid data')
|
||||
self.assertEqual(self.session.decode(encoded), {})
|
||||
|
||||
def test_actual_expiry(self):
|
||||
# this doesn't work with JSONSerializer (serializing timedelta)
|
||||
with override_settings(SESSION_SERIALIZER='django.contrib.sessions.serializers.PickleSerializer'):
|
||||
|
|
Loading…
Reference in New Issue