Fixed #31274 -- Used signing infrastructure in SessionBase.encode()/decode().

Thanks Mariusz Felisiak and Florian Apolloner for the reviews.
This commit is contained in:
Claude Paroz 2020-02-15 12:20:37 +01:00 committed by Mariusz Felisiak
parent daaa894960
commit d4fff711d4
4 changed files with 37 additions and 3 deletions

View File

@ -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 ':'

View File

@ -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``.

View File

@ -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

View File

@ -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: