Fixed #31274 -- Used signing infrastructure in SessionBase.encode()/decode().
Thanks Mariusz Felisiak and Florian Apolloner for the reviews.
This commit is contained in:
parent
daaa894960
commit
d4fff711d4
|
@ -6,6 +6,7 @@ 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 (
|
||||
|
@ -71,6 +72,10 @@ class SessionBase:
|
|||
del self._session[key]
|
||||
self.modified = True
|
||||
|
||||
@property
|
||||
def key_salt(self):
|
||||
return 'django.contrib.sessions.' + self.__class__.__qualname__
|
||||
|
||||
def get(self, key, default=None):
|
||||
return self._session.get(key, default)
|
||||
|
||||
|
@ -97,16 +102,27 @@ class SessionBase:
|
|||
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."
|
||||
serialized = self.serializer().dumps(session_dict)
|
||||
hash = self._hash(serialized)
|
||||
return base64.b64encode(hash.encode() + b":" + serialized).decode('ascii')
|
||||
return signing.dumps(
|
||||
session_dict, salt=self.key_salt, serializer=self.serializer,
|
||||
compress=True,
|
||||
)
|
||||
|
||||
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 Exception:
|
||||
return self._legacy_decode(session_data)
|
||||
|
||||
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 ':'
|
||||
|
|
|
@ -52,6 +52,8 @@ details on these changes.
|
|||
* Support for the pre-Django 3.1 password reset tokens in the admin site (that
|
||||
use the SHA-1 hashing algorithm) will be removed.
|
||||
|
||||
* Support for the pre-Django 3.1 encoding format of sessions will be removed.
|
||||
|
||||
* The ``get_request`` argument for
|
||||
``django.utils.deprecation.MiddlewareMixin.__init__()`` will be required and
|
||||
won't accept ``None``.
|
||||
|
|
|
@ -539,6 +539,10 @@ Miscellaneous
|
|||
from the format generated by older versions of Django. Support for the old
|
||||
format remains until Django 4.0.
|
||||
|
||||
* The encoding format of sessions is different from the format generated by
|
||||
older versions of Django. Support for the old format remains until Django
|
||||
4.0.
|
||||
|
||||
.. _removed-features-3.1:
|
||||
|
||||
Features removed in 3.1
|
||||
|
|
|
@ -311,6 +311,18 @@ 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'},
|
||||
)
|
||||
|
||||
def test_decode_failure_logged_to_security(self):
|
||||
bad_encode = base64.b64encode(b'flaskdj:alkdjf').decode('ascii')
|
||||
with self.assertLogs('django.security.SuspiciousSession', 'WARNING') as cm:
|
||||
|
|
Loading…
Reference in New Issue