Răsfoiți Sursa

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

Thanks Mariusz Felisiak and Florian Apolloner for the reviews.
Claude Paroz 5 ani în urmă
părinte
comite
d4fff711d4

+ 19 - 3
django/contrib/sessions/backends/base.py

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

+ 2 - 0
docs/internals/deprecation.txt

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

+ 4 - 0
docs/releases/3.1.txt

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

+ 12 - 0
tests/sessions_tests/tests.py

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