Michael Merickel
2018-10-15 81576ee51564c49d5ff3c1c07f214f22a8438231
commit | author | age
968209 1 import base64
319793 2 import binascii
10c685 3 import hashlib
319793 4 import hmac
CM 5 import os
10c685 6 import time
c31883 7 import warnings
968209 8
c151ad 9 from zope.deprecation import deprecated
3b7334 10 from zope.interface import implementer
6af3eb 11
c31883 12 from webob.cookies import (
MM 13     JSONSerializer,
14     SignedSerializer,
15 )
8134a7 16
0c1c39 17 from pyramid.compat import (
CM 18     pickle,
bc37a5 19     PY2,
0c1c39 20     text_,
CM 21     bytes_,
22     native_,
23     )
682a9b 24 from pyramid.csrf import (
MM 25     check_csrf_origin,
26     check_csrf_token,
27 )
0c1c39 28
6af3eb 29 from pyramid.interfaces import ISession
a2c7c7 30 from pyramid.util import strings_differ
MW 31
6af3eb 32
968209 33 def manage_accessed(wrapped):
4fade6 34     """ Decorator which causes a cookie to be renewed when an accessor
MM 35     method is called."""
968209 36     def accessed(session, *arg, **kw):
4fade6 37         session.accessed = now = int(time.time())
b2dd47 38         if session._reissue_time is not None:
MM 39             if now - session.renewed > session._reissue_time:
40                 session.changed()
968209 41         return wrapped(session, *arg, **kw)
CM 42     accessed.__doc__ = wrapped.__doc__
43     return accessed
4fade6 44
MM 45 def manage_changed(wrapped):
46     """ Decorator which causes a cookie to be set when a setter method
47     is called."""
48     def changed(session, *arg, **kw):
49         session.accessed = int(time.time())
50         session.changed()
51         return wrapped(session, *arg, **kw)
52     changed.__doc__ = wrapped.__doc__
53     return changed
968209 54
cf46a1 55 def signed_serialize(data, secret):
IW 56     """ Serialize any pickleable structure (``data``) and sign it
57     using the ``secret`` (must be a string).  Return the
58     serialization, which includes the signature as its first 40 bytes.
59     The ``signed_deserialize`` method will deserialize such a value.
60
61     This function is useful for creating signed cookies.  For example:
62
63     .. code-block:: python
64
65        cookieval = signed_serialize({'a':1}, 'secret')
66        response.set_cookie('signed_cookie', cookieval)
ba5ca6 67
MM 68     .. deprecated:: 1.10
69
70        This function will be removed in :app:`Pyramid` 2.0. It is using
71        pickle-based serialization, which is considered vulnerable to remote
72        code execution attacks and will no longer be used by the default
73        session factories at that time.
74
cf46a1 75     """
IW 76     pickled = pickle.dumps(data, pickle.HIGHEST_PROTOCOL)
cf026e 77     try:
MM 78         # bw-compat with pyramid <= 1.5b1 where latin1 is the default
79         secret = bytes_(secret)
80     except UnicodeEncodeError:
81         secret = bytes_(secret, 'utf-8')
82     sig = hmac.new(secret, pickled, hashlib.sha1).hexdigest()
cf46a1 83     return sig + native_(base64.b64encode(pickled))
ba5ca6 84
MM 85 deprecated(
86     'signed_serialize',
87     'This function will be removed in Pyramid 2.0. It is using pickle-based '
88     'serialization, which is considered vulnerable to remote code execution '
89     'attacks.',
90 )
cf46a1 91
IW 92 def signed_deserialize(serialized, secret, hmac=hmac):
93     """ Deserialize the value returned from ``signed_serialize``.  If
94     the value cannot be deserialized for any reason, a
95     :exc:`ValueError` exception will be raised.
96
97     This function is useful for deserializing a signed cookie value
98     created by ``signed_serialize``.  For example:
99
100     .. code-block:: python
101
102        cookieval = request.cookies['signed_cookie']
103        data = signed_deserialize(cookieval, 'secret')
ba5ca6 104
MM 105     .. deprecated:: 1.10
106
107        This function will be removed in :app:`Pyramid` 2.0. It is using
108        pickle-based serialization, which is considered vulnerable to remote
109        code execution attacks and will no longer be used by the default
110        session factories at that time.
cf46a1 111     """
IW 112     # hmac parameterized only for unit tests
113     try:
4fade6 114         input_sig, pickled = (bytes_(serialized[:40]),
cf46a1 115                               base64.b64decode(bytes_(serialized[40:])))
IW 116     except (binascii.Error, TypeError) as e:
117         # Badly formed data can make base64 die
118         raise ValueError('Badly formed base64 data: %s' % e)
119
cf026e 120     try:
MM 121         # bw-compat with pyramid <= 1.5b1 where latin1 is the default
122         secret = bytes_(secret)
123     except UnicodeEncodeError:
124         secret = bytes_(secret, 'utf-8')
125     sig = bytes_(hmac.new(secret, pickled, hashlib.sha1).hexdigest())
cf46a1 126
IW 127     # Avoid timing attacks (see
128     # http://seb.dbzteam.org/crypto/python-oauth-timing-hmac.pdf)
129     if strings_differ(sig, input_sig):
130         raise ValueError('Invalid signature')
131
132     return pickle.loads(pickled)
133
ba5ca6 134 deprecated(
MM 135     'signed_deserialize',
136     'This function will be removed in Pyramid 2.0. It is using pickle-based '
137     'serialization, which is considered vulnerable to remote code execution '
138     'attacks.',
139 )
140
68c00d 141
8134a7 142 class PickleSerializer(object):
ee9c62 143     """ A serializer that uses the pickle protocol to dump Python
MM 144     data to bytes.
145
146     This is the default serializer used by Pyramid.
147
148     ``protocol`` may be specified to control the version of pickle used.
149     Defaults to :attr:`pickle.HIGHEST_PROTOCOL`.
150
151     """
152     def __init__(self, protocol=pickle.HIGHEST_PROTOCOL):
153         self.protocol = protocol
154
8134a7 155     def loads(self, bstruct):
ee9c62 156         """Accept bytes and return a Python object."""
a9cd71 157         try:
MM 158             return pickle.loads(bstruct)
159         # at least ValueError, AttributeError, ImportError but more to be safe
160         except Exception:
161             raise ValueError
8134a7 162
CM 163     def dumps(self, appstruct):
ee9c62 164         """Accept a Python object and return bytes."""
MM 165         return pickle.dumps(appstruct, self.protocol)
8134a7 166
c31883 167
MM 168 JSONSerializer = JSONSerializer  # api
169
170
4fade6 171 def BaseCookieSessionFactory(
8134a7 172     serializer,
4fade6 173     cookie_name='session',
MM 174     max_age=None,
175     path='/',
176     domain=None,
177     secure=False,
178     httponly=False,
6c8ad4 179     samesite='Lax',
4fade6 180     timeout=1200,
MM 181     reissue_time=0,
182     set_on_exception=True,
183     ):
184     """
185     Configure a :term:`session factory` which will provide cookie-based
9536f9 186     sessions.  The return value of this function is a :term:`session factory`,
CM 187     which may be provided as the ``session_factory`` argument of a
188     :class:`pyramid.config.Configurator` constructor, or used as the
189     ``session_factory`` argument of the
4fade6 190     :meth:`pyramid.config.Configurator.set_session_factory` method.
MM 191
192     The session factory returned by this function will create sessions
193     which are limited to storing fewer than 4000 bytes of data (as the
194     payload must fit into a single cookie).
195
196     .. warning:
197
198        This class provides no protection from tampering and is only intended
199        to be used by framework authors to create their own cookie-based
200        session factories.
201
202     Parameters:
203
8134a7 204     ``serializer``
1098ac 205       An object with two methods: ``loads`` and ``dumps``.  The ``loads``
MM 206       method should accept bytes and return a Python object.  The ``dumps``
207       method should accept a Python object and return bytes.  A ``ValueError``
208       should be raised for malformed inputs.
4fade6 209
MM 210     ``cookie_name``
211       The name of the cookie used for sessioning. Default: ``'session'``.
212
213     ``max_age``
214       The maximum age of the cookie used for sessioning (in seconds).
215       Default: ``None`` (browser scope).
216
217     ``path``
218       The path used for the session cookie. Default: ``'/'``.
219
220     ``domain``
221       The domain used for the session cookie.  Default: ``None`` (no domain).
222
223     ``secure``
224       The 'secure' flag of the session cookie. Default: ``False``.
225
226     ``httponly``
227       Hide the cookie from Javascript by setting the 'HttpOnly' flag of the
228       session cookie. Default: ``False``.
229
2d9390 230     ``samesite``
beafcc 231       The 'samesite' option of the session cookie. Set the value to ``None``
MM 232       to turn off the samesite option.  Default: ``'Lax'``.
2d9390 233
4fade6 234     ``timeout``
MM 235       A number of seconds of inactivity before a session times out. If
89dc46 236       ``None`` then the cookie never expires. This lifetime only applies
MM 237       to the *value* within the cookie. Meaning that if the cookie expires
238       due to a lower ``max_age``, then this setting has no effect.
239       Default: ``1200``.
4fade6 240
MM 241     ``reissue_time``
242       The number of seconds that must pass before the cookie is automatically
243       reissued as the result of a request which accesses the session. The
244       duration is measured as the number of seconds since the last session
245       cookie was issued and 'now'.  If this value is ``0``, a new cookie
89dc46 246       will be reissued on every request accessing the session. If ``None``
4fade6 247       then the cookie's lifetime will never be extended.
MM 248
249       A good rule of thumb: if you want auto-expired cookies based on
250       inactivity: set the ``timeout`` value to 1200 (20 mins) and set the
251       ``reissue_time`` value to perhaps a tenth of the ``timeout`` value
252       (120 or 2 mins).  It's nonsensical to set the ``timeout`` value lower
253       than the ``reissue_time`` value, as the ticket will never be reissued.
254       However, such a configuration is not explicitly prevented.
255
256       Default: ``0``.
257
258     ``set_on_exception``
259       If ``True``, set a session cookie even if an exception occurs
260       while rendering a view. Default: ``True``.
261
262     .. versionadded: 1.5a3
beafcc 263
MM 264     .. versionchanged: 1.10
265
266        Added the ``samesite`` option and made the default ``'Lax'``.
4fade6 267     """
MM 268
269     @implementer(ISession)
270     class CookieSession(dict):
271         """ Dictionary-like session object """
272
273         # configuration parameters
274         _cookie_name = cookie_name
fa7886 275         _cookie_max_age = max_age if max_age is None else int(max_age)
4fade6 276         _cookie_path = path
MM 277         _cookie_domain = domain
278         _cookie_secure = secure
279         _cookie_httponly = httponly
2d9390 280         _cookie_samesite = samesite
4fade6 281         _cookie_on_exception = set_on_exception
67ff3b 282         _timeout = timeout if timeout is None else int(timeout)
a43abd 283         _reissue_time = reissue_time if reissue_time is None else int(reissue_time)
4fade6 284
MM 285         # dirty flag
286         _dirty = False
287
288         def __init__(self, request):
289             self.request = request
290             now = time.time()
291             created = renewed = now
292             new = True
293             value = None
294             state = {}
295             cookieval = request.cookies.get(self._cookie_name)
296             if cookieval is not None:
297                 try:
8134a7 298                     value = serializer.loads(bytes_(cookieval))
4fade6 299                 except ValueError:
b0b09c 300                     # the cookie failed to deserialize, dropped
4fade6 301                     value = None
MM 302
303             if value is not None:
304                 try:
8f4fbd 305                     # since the value is not necessarily signed, we have
MM 306                     # to unpack it a little carefully
307                     rval, cval, sval = value
308                     renewed = float(rval)
309                     created = float(cval)
310                     state = sval
4fade6 311                     new = False
8f4fbd 312                 except (TypeError, ValueError):
b0b09c 313                     # value failed to unpack properly or renewed was not
MM 314                     # a numeric type so we'll fail deserialization here
4fade6 315                     state = {}
MM 316
8f4fbd 317             if self._timeout is not None:
MM 318                 if now - renewed > self._timeout:
319                     # expire the session because it was not renewed
320                     # before the timeout threshold
4fade6 321                     state = {}
MM 322
323             self.created = created
324             self.accessed = renewed
325             self.renewed = renewed
326             self.new = new
327             dict.__init__(self, state)
328
329         # ISession methods
330         def changed(self):
331             if not self._dirty:
332                 self._dirty = True
333                 def set_cookie_callback(request, response):
334                     self._set_cookie(response)
335                     self.request = None # explicitly break cycle for gc
336                 self.request.add_response_callback(set_cookie_callback)
337
338         def invalidate(self):
339             self.clear() # XXX probably needs to unset cookie
340
341         # non-modifying dictionary methods
342         get = manage_accessed(dict.get)
343         __getitem__ = manage_accessed(dict.__getitem__)
344         items = manage_accessed(dict.items)
345         values = manage_accessed(dict.values)
346         keys = manage_accessed(dict.keys)
347         __contains__ = manage_accessed(dict.__contains__)
348         __len__ = manage_accessed(dict.__len__)
349         __iter__ = manage_accessed(dict.__iter__)
350
bc37a5 351         if PY2:
4fade6 352             iteritems = manage_accessed(dict.iteritems)
MM 353             itervalues = manage_accessed(dict.itervalues)
354             iterkeys = manage_accessed(dict.iterkeys)
355             has_key = manage_accessed(dict.has_key)
356
357         # modifying dictionary methods
358         clear = manage_changed(dict.clear)
359         update = manage_changed(dict.update)
360         setdefault = manage_changed(dict.setdefault)
361         pop = manage_changed(dict.pop)
362         popitem = manage_changed(dict.popitem)
363         __setitem__ = manage_changed(dict.__setitem__)
364         __delitem__ = manage_changed(dict.__delitem__)
365
366         # flash API methods
367         @manage_changed
368         def flash(self, msg, queue='', allow_duplicate=True):
369             storage = self.setdefault('_f_' + queue, [])
370             if allow_duplicate or (msg not in storage):
371                 storage.append(msg)
372
373         @manage_changed
374         def pop_flash(self, queue=''):
375             storage = self.pop('_f_' + queue, [])
376             return storage
377
378         @manage_accessed
379         def peek_flash(self, queue=''):
380             storage = self.get('_f_' + queue, [])
381             return storage
382
383         # CSRF API methods
384         @manage_changed
385         def new_csrf_token(self):
386             token = text_(binascii.hexlify(os.urandom(20)))
387             self['_csrft_'] = token
388             return token
389
390         @manage_accessed
391         def get_csrf_token(self):
392             token = self.get('_csrft_', None)
393             if token is None:
394                 token = self.new_csrf_token()
395             return token
396
397         # non-API methods
398         def _set_cookie(self, response):
399             if not self._cookie_on_exception:
400                 exception = getattr(self.request, 'exception', None)
401                 if exception is not None: # dont set a cookie during exceptions
402                     return False
8134a7 403             cookieval = native_(serializer.dumps(
4fade6 404                 (self.accessed, self.created, dict(self))
MM 405                 ))
406             if len(cookieval) > 4064:
407                 raise ValueError(
408                     'Cookie value is too long to store (%s bytes)' %
409                     len(cookieval)
410                     )
411             response.set_cookie(
412                 self._cookie_name,
413                 value=cookieval,
414                 max_age=self._cookie_max_age,
415                 path=self._cookie_path,
416                 domain=self._cookie_domain,
417                 secure=self._cookie_secure,
418                 httponly=self._cookie_httponly,
2d9390 419                 samesite=self._cookie_samesite,
4fade6 420                 )
MM 421             return True
422
423     return CookieSession
968209 424
c151ad 425
MM 426 def UnencryptedCookieSessionFactoryConfig(
427     secret,
428     timeout=1200,
429     cookie_name='session',
430     cookie_max_age=None,
431     cookie_path='/',
432     cookie_domain=None,
433     cookie_secure=False,
434     cookie_httponly=False,
6c8ad4 435     cookie_samesite='Lax',
c151ad 436     cookie_on_exception=True,
MM 437     signed_serialize=signed_serialize,
438     signed_deserialize=signed_deserialize,
439     ):
440     """
441     .. deprecated:: 1.5
442         Use :func:`pyramid.session.SignedCookieSessionFactory` instead.
443         Caveat: Cookies generated using ``SignedCookieSessionFactory`` are not
444         compatible with cookies generated using
445         ``UnencryptedCookieSessionFactory``, so existing user session data
446         will be destroyed if you switch to it.
13734a 447
c151ad 448     Configure a :term:`session factory` which will provide unencrypted
MM 449     (but signed) cookie-based sessions.  The return value of this
450     function is a :term:`session factory`, which may be provided as
451     the ``session_factory`` argument of a
452     :class:`pyramid.config.Configurator` constructor, or used
453     as the ``session_factory`` argument of the
454     :meth:`pyramid.config.Configurator.set_session_factory`
455     method.
456
457     The session factory returned by this function will create sessions
458     which are limited to storing fewer than 4000 bytes of data (as the
459     payload must fit into a single cookie).
460
461     Parameters:
462
463     ``secret``
464       A string which is used to sign the cookie.
465
466     ``timeout``
467       A number of seconds of inactivity before a session times out.
468
469     ``cookie_name``
470       The name of the cookie used for sessioning.
471
472     ``cookie_max_age``
473       The maximum age of the cookie used for sessioning (in seconds).
474       Default: ``None`` (browser scope).
475
476     ``cookie_path``
477       The path used for the session cookie.
478
479     ``cookie_domain``
480       The domain used for the session cookie.  Default: ``None`` (no domain).
481
482     ``cookie_secure``
483       The 'secure' flag of the session cookie.
484
485     ``cookie_httponly``
486       The 'httpOnly' flag of the session cookie.
487
2d9390 488     ``cookie_samesite``
beafcc 489       The 'samesite' option of the session cookie. Set the value to ``None``
MM 490       to turn off the samesite option.  Default: ``'Lax'``.
2d9390 491
c151ad 492     ``cookie_on_exception``
MM 493       If ``True``, set a session cookie even if an exception occurs
494       while rendering a view.
495
496     ``signed_serialize``
497       A callable which takes more or less arbitrary Python data structure and
498       a secret and returns a signed serialization in bytes.
499       Default: ``signed_serialize`` (using pickle).
500
501     ``signed_deserialize``
502       A callable which takes a signed and serialized data structure in bytes
503       and a secret and returns the original data structure if the signature
504       is valid. Default: ``signed_deserialize`` (using pickle).
beafcc 505
MM 506     .. versionchanged: 1.10
507
508        Added the ``samesite`` option and made the default ``'Lax'``.
c151ad 509     """
MM 510
511     class SerializerWrapper(object):
512         def __init__(self, secret):
513             self.secret = secret
13734a 514
c151ad 515         def loads(self, bstruct):
MM 516             return signed_deserialize(bstruct, secret)
517
518         def dumps(self, appstruct):
519             return signed_serialize(appstruct, secret)
520
521     serializer = SerializerWrapper(secret)
522
523     return BaseCookieSessionFactory(
524         serializer,
525         cookie_name=cookie_name,
526         max_age=cookie_max_age,
527         path=cookie_path,
528         domain=cookie_domain,
529         secure=cookie_secure,
530         httponly=cookie_httponly,
2d9390 531         samesite=cookie_samesite,
c151ad 532         timeout=timeout,
MM 533         reissue_time=0, # to keep session.accessed == session.renewed
534         set_on_exception=cookie_on_exception,
535     )
536
537 deprecated(
538     'UnencryptedCookieSessionFactoryConfig',
539     'The UnencryptedCookieSessionFactoryConfig callable is deprecated as of '
540     'Pyramid 1.5.  Use ``pyramid.session.SignedCookieSessionFactory`` instead.'
541     ' Caveat: Cookies generated using SignedCookieSessionFactory are not '
542     'compatible with cookies generated using UnencryptedCookieSessionFactory, '
543     'so existing user session data will be destroyed if you switch to it.'
544     )
545
c31883 546
3a6cbc 547 def SignedCookieSessionFactory(
MM 548     secret,
549     cookie_name='session',
4fade6 550     max_age=None,
MM 551     path='/',
552     domain=None,
553     secure=False,
554     httponly=False,
6c8ad4 555     samesite='Lax',
4fade6 556     set_on_exception=True,
MM 557     timeout=1200,
558     reissue_time=0,
559     hashalg='sha512',
b0b09c 560     salt='pyramid.session.',
8134a7 561     serializer=None,
3a6cbc 562     ):
MM 563     """
9536f9 564     .. versionadded:: 1.5
13734a 565
3a6cbc 566     Configure a :term:`session factory` which will provide signed
MM 567     cookie-based sessions.  The return value of this
568     function is a :term:`session factory`, which may be provided as
569     the ``session_factory`` argument of a
570     :class:`pyramid.config.Configurator` constructor, or used
571     as the ``session_factory`` argument of the
572     :meth:`pyramid.config.Configurator.set_session_factory`
573     method.
968209 574
3a6cbc 575     The session factory returned by this function will create sessions
MM 576     which are limited to storing fewer than 4000 bytes of data (as the
577     payload must fit into a single cookie).
815955 578
3a6cbc 579     Parameters:
968209 580
3a6cbc 581     ``secret``
b0b09c 582       A string which is used to sign the cookie. The secret should be at
MM 583       least as long as the block size of the selected hash algorithm. For
13734a 584       ``sha512`` this would mean a 512 bit (64 character) secret.  It should
e521f1 585       be unique within the set of secret values provided to Pyramid for
CM 586       its various subsystems (see :ref:`admonishment_against_secret_sharing`).
968209 587
3a6cbc 588     ``hashalg``
MM 589       The HMAC digest algorithm to use for signing. The algorithm must be
590       supported by the :mod:`hashlib` library. Default: ``'sha512'``.
968209 591
b0b09c 592     ``salt``
MM 593       A namespace to avoid collisions between different uses of a shared
594       secret. Reusing a secret for different parts of an application is
2dea18 595       strongly discouraged (see :ref:`admonishment_against_secret_sharing`).
7c756b 596       Default: ``'pyramid.session.'``.
968209 597
3a6cbc 598     ``cookie_name``
MM 599       The name of the cookie used for sessioning. Default: ``'session'``.
968209 600
4fade6 601     ``max_age``
3a6cbc 602       The maximum age of the cookie used for sessioning (in seconds).
MM 603       Default: ``None`` (browser scope).
475532 604
4fade6 605     ``path``
3a6cbc 606       The path used for the session cookie. Default: ``'/'``.
968209 607
4fade6 608     ``domain``
3a6cbc 609       The domain used for the session cookie.  Default: ``None`` (no domain).
4df636 610
4fade6 611     ``secure``
3a6cbc 612       The 'secure' flag of the session cookie. Default: ``False``.
6f6d36 613
4fade6 614     ``httponly``
MM 615       Hide the cookie from Javascript by setting the 'HttpOnly' flag of the
616       session cookie. Default: ``False``.
4df636 617
2d9390 618     ``samesite``
beafcc 619       The 'samesite' option of the session cookie. Set the value to ``None``
MM 620       to turn off the samesite option.  Default: ``'Lax'``.
2d9390 621
4fade6 622     ``timeout``
MM 623       A number of seconds of inactivity before a session times out. If
89dc46 624       ``None`` then the cookie never expires. This lifetime only applies
MM 625       to the *value* within the cookie. Meaning that if the cookie expires
626       due to a lower ``max_age``, then this setting has no effect.
627       Default: ``1200``.
319793 628
4fade6 629     ``reissue_time``
MM 630       The number of seconds that must pass before the cookie is automatically
89dc46 631       reissued as the result of accessing the session. The
4fade6 632       duration is measured as the number of seconds since the last session
MM 633       cookie was issued and 'now'.  If this value is ``0``, a new cookie
89dc46 634       will be reissued on every request accessing the session. If ``None``
4fade6 635       then the cookie's lifetime will never be extended.
319793 636
4fade6 637       A good rule of thumb: if you want auto-expired cookies based on
MM 638       inactivity: set the ``timeout`` value to 1200 (20 mins) and set the
639       ``reissue_time`` value to perhaps a tenth of the ``timeout`` value
640       (120 or 2 mins).  It's nonsensical to set the ``timeout`` value lower
641       than the ``reissue_time`` value, as the ticket will never be reissued.
642       However, such a configuration is not explicitly prevented.
968209 643
4fade6 644       Default: ``0``.
MM 645
646     ``set_on_exception``
3a6cbc 647       If ``True``, set a session cookie even if an exception occurs
MM 648       while rendering a view. Default: ``True``.
649
8134a7 650     ``serializer``
1098ac 651       An object with two methods: ``loads`` and ``dumps``.  The ``loads``
MM 652       method should accept bytes and return a Python object.  The ``dumps``
653       method should accept a Python object and return bytes.  A ``ValueError``
654       should be raised for malformed inputs.  If a serializer is not passed,
655       the :class:`pyramid.session.PickleSerializer` serializer will be used.
4fade6 656
c31883 657     .. warning::
MM 658
659        In :app:`Pyramid` 2.0 the default ``serializer`` option will change to
660        use :class:`pyramid.session.JSONSerializer`. See
661        :ref:`pickle_session_deprecation` for more information about why this
662        change is being made.
663
4fade6 664     .. versionadded: 1.5a3
beafcc 665
MM 666     .. versionchanged: 1.10
667
668        Added the ``samesite`` option and made the default ``Lax``.
c31883 669
3a6cbc 670     """
8134a7 671     if serializer is None:
CM 672         serializer = PickleSerializer()
c31883 673         warnings.warn(
MM 674             'The default pickle serializer is deprecated as of Pyramid 1.9 '
675             'and it will be changed to use pyramid.session.JSONSerializer in '
676             'version 2.0. Explicitly set the serializer to avoid future '
677             'incompatibilities. See "Upcoming Changes to ISession in '
678             'Pyramid 2.0" for more information about this change.',
679             DeprecationWarning,
680             stacklevel=1,
681         )
3a6cbc 682
8134a7 683     signed_serializer = SignedSerializer(
CM 684         secret,
1098ac 685         salt,
8134a7 686         hashalg,
CM 687         serializer=serializer,
688         )
3a6cbc 689
4fade6 690     return BaseCookieSessionFactory(
8134a7 691         signed_serializer,
3a6cbc 692         cookie_name=cookie_name,
4fade6 693         max_age=max_age,
MM 694         path=path,
695         domain=domain,
696         secure=secure,
697         httponly=httponly,
2d9390 698         samesite=samesite,
4fade6 699         timeout=timeout,
MM 700         reissue_time=reissue_time,
701         set_on_exception=set_on_exception,
3a6cbc 702     )
682a9b 703
MM 704 check_csrf_origin = check_csrf_origin  # api
705 deprecated('check_csrf_origin',
706            'pyramid.session.check_csrf_origin is deprecated as of Pyramid '
707            '1.9. Use pyramid.csrf.check_csrf_origin instead.')
708
709 check_csrf_token = check_csrf_token  # api
710 deprecated('check_csrf_token',
711            'pyramid.session.check_csrf_token is deprecated as of Pyramid '
712            '1.9. Use pyramid.csrf.check_csrf_token instead.')