def signed_serialize(data, secret): """Serialize any pickleable structure (``data``) and sign it using the ``secret`` (must be a string). Return the serialization, which includes the signature as its first 40 bytes. The ``signed_deserialize`` method will deserialize such a value. This function is useful for creating signed cookies. For example: .. code-block:: python cookieval = signed_serialize({'a':1}, 'secret') response.set_cookie('signed_cookie', cookieval) .. deprecated:: 1.10 This function will be removed in :app:`Pyramid` 2.0. It is using pickle-based serialization, which is considered vulnerable to remote code execution attacks and will no longer be used by the default session factories at that time. """ pickled = pickle.dumps(data, pickle.HIGHEST_PROTOCOL) try: # bw-compat with pyramid <= 1.5b1 where latin1 is the default secret = bytes_(secret) except UnicodeEncodeError: secret = bytes_(secret, 'utf-8') sig = hmac.new(secret, pickled, hashlib.sha1).hexdigest() return sig + native_(base64.b64encode(pickled))
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)
def _set_cookie(self, response): if not self._cookie_on_exception: exception = getattr(self.request, 'exception', None) if exception is not None: # dont set a cookie during exceptions return False cookieval = self.new and get_random() or self._get_cookie() if not cookieval: return False value = self._to_pickle and pickle.dumps(dict(self)) or dict(self) data = dict(accessed=self.accessed, created=self.created, value=value, pickled=self._to_pickle, _id=cookieval) self._collection.replace_one({'_id': cookieval}, data, upsert=True) response.set_cookie(self._cookie_name, value=cookieval, max_age=self._cookie_max_age, path=self._cookie_path, domain=self._cookie_domain, secure=self._cookie_secure, httponly=self._cookie_httponly) return True
def _set_cookie(self, response): # Save the value in the database data = Binary(pickle.dumps(dict(self))) sessioneid = self.sessioneid with self.request.registry['cubicweb.repository'].internal_cnx( ) as cnx: if not sessioneid: session = cnx.create_entity('CWSession', cwsessiondata=data) sessioneid = session.eid else: try: session = cnx.entity_from_eid(sessioneid) except UnknownEid: # Might occur if CWSession entity got dropped (e.g. # the whole db got recreated) while user's cookie is # still valid. We recreate the CWSession in this case. sessioneid = cnx.create_entity('CWSession', cwsessiondata=data).eid else: session.cw_set(cwsessiondata=data) cnx.commit() # Only if needed actually set the cookie if self.new or self.accessed - self.renewed > self._reissue_time: dict.clear(self) dict.__setitem__(self, 'sessioneid', sessioneid) return super(CWSession, self)._set_cookie(response) return True
def _set_cookie(self, response): if not self._cookie_on_exception: exception = getattr(self.request, 'exception', None) if exception is not None: # dont set a cookie during exceptions return False cookieval = self.new and get_random() or self._get_cookie() if not cookieval: return False value = self._to_pickle and pickle.dumps(dict(self)) or dict(self) data = dict(accessed=self.accessed, created=self.created, value=value, pickled=self._to_pickle, _id=cookieval) self._collection.replace_one({'_id': cookieval}, data, upsert=True) response.set_cookie( self._cookie_name, value=cookieval, max_age=self._cookie_max_age, path=self._cookie_path, domain=self._cookie_domain, secure=self._cookie_secure, httponly=self._cookie_httponly) return True
def _serialize(self, value, secret, nonce): import base64 from pyramid.compat import pickle from nacl.secret import SecretBox cstruct = pickle.dumps(value, pickle.HIGHEST_PROTOCOL) fstruct = SecretBox(secret).encrypt(cstruct, nonce) return base64.urlsafe_b64encode(fstruct).rstrip(b"=")
def _serialize(self, value, secret, nonce): import base64 from pyramid.compat import pickle from nacl.secret import SecretBox cstruct = pickle.dumps(value, pickle.HIGHEST_PROTOCOL) fstruct = SecretBox(secret).encrypt(cstruct, nonce) return base64.urlsafe_b64encode(fstruct).rstrip(b'=')
def serialize(data, secret): import hmac import base64 from hashlib import sha1 from pyramid.compat import bytes_ from pyramid.compat import native_ from pyramid.compat import pickle pickled = pickle.dumps(data, pickle.HIGHEST_PROTOCOL) sig = hmac.new(bytes_(secret), pickled, sha1).hexdigest() return sig + native_(base64.b64encode(pickled))
def _serialize(self, value, salt=b'pyramid.session.', hashalg='sha512'): import base64 import hashlib import hmac import pickle digestmod = lambda: hashlib.new(hashalg) cstruct = pickle.dumps(value, pickle.HIGHEST_PROTOCOL) sig = hmac.new(salt + b'secret', cstruct, digestmod).digest() return base64.urlsafe_b64encode(sig + cstruct).rstrip(b'=')
def _serialize(self, value): import os import hashlib cookieval = hashlib.md5(os.urandom(32)).hexdigest() val = pickle.dumps(value['value']) value.update({'_id': cookieval, 'value': val}) collection.replace_one({'_id': cookieval}, value, upsert=True) return cookieval
def dumps(self, session_state): """Encrypt session state. :type session_state: :class:`dict` / picklable mapping :param session_state: the session state to be encrypted. :rtype: bytes :returns: the encrypted session state """ pickled = pickle.dumps(session_state) nonce = random(SecretBox.NONCE_SIZE) return urlsafe_b64encode(self.box.encrypt(pickled, nonce))
def test_loads_with_tampered_content(self): import base64 from pyramid.compat import pickle from nacl.secret import SecretBox secret = b'SEEKRIT!' * 4 appstruct = {'foo': 'bar'} nonce = b'\x01' * 24 cstruct = pickle.dumps(appstruct, pickle.HIGHEST_PROTOCOL) fstruct = SecretBox(secret).encrypt(cstruct, nonce) fstruct = b'tamper' + fstruct bstruct = base64.urlsafe_b64encode(fstruct).rstrip(b'=') eps = self._makeOne(b'SEEKRIT!' * 4) self.assertRaises(ValueError, eps.loads, bstruct)
def test_loads_with_tampered_content(self): import base64 from pyramid.compat import pickle from nacl.secret import SecretBox secret = b"SEEKRIT!" * 4 appstruct = {"foo": "bar"} nonce = b"\x01" * 24 cstruct = pickle.dumps(appstruct, pickle.HIGHEST_PROTOCOL) fstruct = SecretBox(secret).encrypt(cstruct, nonce) fstruct = b"tamper" + fstruct bstruct = base64.urlsafe_b64encode(fstruct).rstrip(b"=") eps = self._makeOne(b"SEEKRIT!" * 4) self.assertRaises(ValueError, eps.loads, bstruct)
def signed_serialize(data, secret): """ Serialize any pickleable structure (``data``) and sign it using the ``secret`` (must be a string). Return the serialization, which includes the signature as its first 40 bytes. The ``signed_deserialize`` method will deserialize such a value. This function is useful for creating signed cookies. For example: .. code-block:: python cookieval = signed_serialize({'a':1}, 'secret') response.set_cookie('signed_cookie', cookieval) """ pickled = pickle.dumps(data, pickle.HIGHEST_PROTOCOL) sig = hmac.new(bytes_(secret), pickled, sha1).hexdigest() return sig + native_(base64.b64encode(pickled))
def test_loads(self): from pyramid.compat import pickle from .. import serializer as MUT SECRET = 'SEEKRIT!' * 4 # 32 bytes NONCE = b'\x01' * 24 APPSTRUCT = {'foo': 'bar'} PICKLED = pickle.dumps(APPSTRUCT) CIPHERTEXT = PICKLED + b':' + NONCE _base64_called = [] def _base64_decode(what): _base64_called.append(what) return what with _Monkey(MUT, SecretBox=_SecretBox, urlsafe_b64decode=_base64_decode): eps = self._makeOne(SECRET) loaded = eps.loads(CIPHERTEXT) self.assertEqual(loaded, APPSTRUCT) self.assertEqual(_base64_called, [CIPHERTEXT])
def signed_serialize(data, secret): """ Serialize any pickleable structure (``data``) and sign it using the ``secret`` (must be a string). Return the serialization, which includes the signature as its first 40 bytes. The ``signed_deserialize`` method will deserialize such a value. This function is useful for creating signed cookies. For example: .. code-block:: python cookieval = signed_serialize({'a':1}, 'secret') response.set_cookie('signed_cookie', cookieval) """ pickled = pickle.dumps(data, pickle.HIGHEST_PROTOCOL) try: # bw-compat with pyramid <= 1.5b1 where latin1 is the default secret = bytes_(secret) except UnicodeEncodeError: secret = bytes_(secret, 'utf-8') sig = hmac.new(secret, pickled, hashlib.sha1).hexdigest() return sig + native_(base64.b64encode(pickled))
def signed_serialize(data, secret): """ Serialize any pickleable structure (``data``) and sign it using the ``secret`` (must be a string). Return the serialization, which includes the signature as its first 40 bytes. The ``signed_deserialize`` method will deserialize such a value. This function is useful for creating signed cookies. For example: .. code-block:: python cookieval = signed_serialize({'a':1}, 'secret') response.set_cookie('signed_cookie', cookieval) """ pickled = pickle.dumps(data, pickle.HIGHEST_PROTOCOL) try: # bw-compat with pyramid <= 1.5b1 where latin1 is the default secret = bytes_(secret) except UnicodeEncodeError: secret = bytes_(secret, "utf-8") sig = hmac.new(secret, pickled, hashlib.sha1).hexdigest() return sig + native_(base64.b64encode(pickled))
def test_dumps(self): from pyramid.compat import pickle from .. import serializer as MUT SECRET = 'SEEKRIT!' * 4 # 32 bytes NONCE = b'\x01' * 24 APPSTRUCT = {'foo': 'bar'} PICKLED = pickle.dumps(APPSTRUCT) _base64_called = [] def _base64_encode(what): _base64_called.append(what) return what with _Monkey(MUT, SecretBox=_SecretBox, random=lambda size: NONCE, urlsafe_b64encode=_base64_encode): eps = self._makeOne(SECRET) encrypted = eps.dumps(APPSTRUCT) pickled, nonce = encrypted[:-25], encrypted[-24:] self.assertEqual(pickled, PICKLED) self.assertEqual(nonce, NONCE) self.assertEqual(_base64_called, [encrypted])
def dumps(self, appstruct): """Accept a Python object and return bytes.""" return pickle.dumps(appstruct, self.protocol)
def dumps(self, appstruct): return pickle.dumps(appstruct, pickle.HIGHEST_PROTOCOL)
def dummy_signed_serialize(data, secret): import base64 from pyramid.compat import pickle, bytes_ pickled = pickle.dumps(data) return base64.b64encode(bytes_(secret)) + base64.b64encode(pickled)
def SignedCookieSessionFactory( secret, cookie_name='session', max_age=None, path='/', domain=None, secure=False, httponly=False, set_on_exception=True, timeout=1200, reissue_time=0, hashalg='sha512', salt='pyramid.session.', serialize=None, deserialize=None, ): """ .. versionadded:: 1.5 Configure a :term:`session factory` which will provide signed cookie-based sessions. The return value of this function is a :term:`session factory`, which may be provided as the ``session_factory`` argument of a :class:`pyramid.config.Configurator` constructor, or used as the ``session_factory`` argument of the :meth:`pyramid.config.Configurator.set_session_factory` method. The session factory returned by this function will create sessions which are limited to storing fewer than 4000 bytes of data (as the payload must fit into a single cookie). Parameters: ``secret`` A string which is used to sign the cookie. The secret should be at least as long as the block size of the selected hash algorithm. For ``sha512`` this would mean a 128 bit (64 character) secret. It should be unique within the set of secret values provided to Pyramid for its various subsystems (see :ref:`admonishment_against_secret_sharing`). ``hashalg`` The HMAC digest algorithm to use for signing. The algorithm must be supported by the :mod:`hashlib` library. Default: ``'sha512'``. ``salt`` A namespace to avoid collisions between different uses of a shared secret. Reusing a secret for different parts of an application is strongly discouraged (see :ref:`admonishment_against_secret_sharing`). Default: ``'pyramid.session.'``. ``cookie_name`` The name of the cookie used for sessioning. Default: ``'session'``. ``max_age`` The maximum age of the cookie used for sessioning (in seconds). Default: ``None`` (browser scope). ``path`` The path used for the session cookie. Default: ``'/'``. ``domain`` The domain used for the session cookie. Default: ``None`` (no domain). ``secure`` The 'secure' flag of the session cookie. Default: ``False``. ``httponly`` Hide the cookie from Javascript by setting the 'HttpOnly' flag of the session cookie. Default: ``False``. ``timeout`` A number of seconds of inactivity before a session times out. If ``None`` then the cookie never expires. Default: 1200. ``reissue_time`` The number of seconds that must pass before the cookie is automatically reissued as the result of a request which accesses the session. The duration is measured as the number of seconds since the last session cookie was issued and 'now'. If this value is ``0``, a new cookie will be reissued on every request accesses the session. If ``None`` then the cookie's lifetime will never be extended. A good rule of thumb: if you want auto-expired cookies based on inactivity: set the ``timeout`` value to 1200 (20 mins) and set the ``reissue_time`` value to perhaps a tenth of the ``timeout`` value (120 or 2 mins). It's nonsensical to set the ``timeout`` value lower than the ``reissue_time`` value, as the ticket will never be reissued. However, such a configuration is not explicitly prevented. Default: ``0``. ``set_on_exception`` If ``True``, set a session cookie even if an exception occurs while rendering a view. Default: ``True``. ``serialize`` A callable accepting a Python object and returning a bytestring. A ``ValueError`` should be raised for malformed inputs. Default: :func:`pickle.dumps`. ``deserialize`` A callable accepting a bytestring and returning a Python object. A ``ValueError`` should be raised for malformed inputs. Default: :func:`pickle.loads`. .. versionadded: 1.5a3 """ if serialize is None: serialize = lambda v: pickle.dumps(v, pickle.HIGHEST_PROTOCOL) if deserialize is None: deserialize = pickle.loads digestmod = lambda string=b'': hashlib.new(hashalg, string) digest_size = digestmod().digest_size salted_secret = bytes_(salt or '') + bytes_(secret) def signed_serialize(appstruct): cstruct = serialize(appstruct) sig = hmac.new(salted_secret, cstruct, digestmod).digest() return base64.b64encode(cstruct + sig) def signed_deserialize(bstruct): try: fstruct = base64.b64decode(bstruct) except (binascii.Error, TypeError) as e: raise ValueError('Badly formed base64 data: %s' % e) cstruct = fstruct[:-digest_size] expected_sig = fstruct[-digest_size:] sig = hmac.new(salted_secret, cstruct, digestmod).digest() if strings_differ(sig, expected_sig): raise ValueError('Invalid signature') return deserialize(cstruct) return BaseCookieSessionFactory( signed_serialize, signed_deserialize, cookie_name=cookie_name, max_age=max_age, path=path, domain=domain, secure=secure, httponly=httponly, timeout=timeout, reissue_time=reissue_time, set_on_exception=set_on_exception, )