From a9cd71dd14eb357bd1a538e2763ab6b78f1bc047 Mon Sep 17 00:00:00 2001 From: Michael Merickel Date: Wed, 8 Aug 2018 00:12:48 -0500 Subject: [PATCH 1/2] catch other pickle errors when loading content --- pyramid/session.py | 6 +++- pyramid/tests/test_session.py | 57 +++++++++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+), 1 deletion(-) diff --git a/pyramid/session.py b/pyramid/session.py index d05ac66eb2..97039a4044 100644 --- a/pyramid/session.py +++ b/pyramid/session.py @@ -121,7 +121,11 @@ def __init__(self, protocol=pickle.HIGHEST_PROTOCOL): def loads(self, bstruct): """Accept bytes and return a Python object.""" - return pickle.loads(bstruct) + try: + return pickle.loads(bstruct) + # at least ValueError, AttributeError, ImportError but more to be safe + except Exception: + raise ValueError def dumps(self, appstruct): """Accept a Python object and return bytes.""" diff --git a/pyramid/tests/test_session.py b/pyramid/tests/test_session.py index e3d819944e..3585ed6353 100644 --- a/pyramid/tests/test_session.py +++ b/pyramid/tests/test_session.py @@ -2,6 +2,7 @@ import json import unittest from pyramid import testing +from pyramid.compat import pickle class SharedCookieSessionTests(object): @@ -462,6 +463,24 @@ def test_very_long_key(self): self.assertEqual(result, None) self.assertTrue('Set-Cookie' in dict(response.headerlist)) + def test_bad_pickle(self): + import base64 + import hashlib + import hmac + + digestmod = lambda: hashlib.new('sha512') + # generated from dumping an object that cannot be found anymore, eg: + # class Foo: pass + # print(pickle.dumps(Foo())) + cstruct = b'(i__main__\nFoo\np0\n(dp1\nb.' + sig = hmac.new(b'pyramid.session.secret', cstruct, digestmod).digest() + cookieval = base64.urlsafe_b64encode(sig + cstruct).rstrip(b'=') + + request = testing.DummyRequest() + request.cookies['session'] = cookieval + session = self._makeOne(request, secret='secret') + self.assertEqual(session, {}) + class TestUnencryptedCookieSession(SharedCookieSessionTests, unittest.TestCase): def setUp(self): super(TestUnencryptedCookieSession, self).setUp() @@ -661,6 +680,44 @@ def test_it_with_latin1_secret(self): self.assertEqual(result, '123') +class TestPickleSerializer(unittest.TestCase): + def _makeOne(self): + from pyramid.session import PickleSerializer + return PickleSerializer() + + def test_loads(self): + # generated from dumping Dummy() using protocol=2 + cstruct = b'\x80\x02cpyramid.tests.test_session\nDummy\nq\x00)\x81q\x01.' + serializer = self._makeOne() + result = serializer.loads(cstruct) + self.assertIsInstance(result, Dummy) + + def test_loads_raises_ValueError_on_invalid_data(self): + cstruct = b'not pickled' + serializer = self._makeOne() + self.assertRaises(ValueError, serializer.loads, cstruct) + + def test_loads_raises_ValueError_on_bad_import(self): + # generated from dumping an object that cannot be found anymore, eg: + # class Foo: pass + # print(pickle.dumps(Foo())) + cstruct = b'(i__main__\nFoo\np0\n(dp1\nb.' + serializer = self._makeOne() + self.assertRaises(ValueError, serializer.loads, cstruct) + + def test_dumps(self): + obj = Dummy() + serializer = self._makeOne() + result = serializer.dumps(obj) + expected_result = pickle.dumps(obj, protocol=pickle.HIGHEST_PROTOCOL) + self.assertEqual(result, expected_result) + self.assertIsInstance(result, bytes) + + +class Dummy(object): + pass + + class DummySerializer(object): def dumps(self, value): return base64.b64encode(json.dumps(value).encode('utf-8')) From 9d6851286695842fc93c1adfc45c6fc8daff73fb Mon Sep 17 00:00:00 2001 From: Michael Merickel Date: Wed, 8 Aug 2018 00:18:17 -0500 Subject: [PATCH 2/2] add changelog for #3325 --- CHANGES.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 6ccd69a472..9106c270f6 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -62,6 +62,12 @@ Bug Fixes code. The old ``MIMEAccept`` has been deprecated. The new methods follow the RFC's more closely. See https://github.com/Pylons/pyramid/pull/3251 +- Catch extra errors like ``AttributeError`` when unpickling "trusted" + session cookies with bad pickle data in them. This would occur when sharing + a secret between projects that shouldn't actually share session cookies, + like when reusing secrets between projects in development. + See https://github.com/Pylons/pyramid/pull/3325 + Deprecations ------------