Skip to content

Commit 99ccd69

Browse files
committed
Merge branch 'master' of https://github.com/bbangert/beaker
2 parents dd67a43 + 7665193 commit 99ccd69

File tree

3 files changed

+191
-59
lines changed

3 files changed

+191
-59
lines changed

beaker/docs/modules/session.rst

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
:mod:`beaker.session` -- Session classes
1+
:mod:`beaker.session` -- Session classes
22
========================================
33

44
.. automodule:: beaker.session
@@ -13,3 +13,4 @@ Module Contents
1313
.. autoclass:: SessionObject
1414
:members: persist, get_by_id, accessed
1515
.. autoclass:: SignedCookie
16+
.. autodata:: InvalidSignature

beaker/session.py

+66-35
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,19 @@
99
from beaker.exceptions import BeakerException, InvalidCryptoBackendError
1010
from beaker.cookie import SimpleCookie
1111

12-
__all__ = ['SignedCookie', 'Session']
12+
__all__ = ['SignedCookie', 'Session', 'InvalidSignature']
13+
14+
15+
class _InvalidSignatureType(object):
16+
"""Returned from SignedCookie when the value's signature was invalid."""
17+
def __nonzero__(self):
18+
return False
19+
20+
def __bool__(self):
21+
return False
22+
23+
24+
InvalidSignature = _InvalidSignatureType()
1325

1426

1527
try:
@@ -50,19 +62,22 @@ def __init__(self, secret, input=None):
5062

5163
def value_decode(self, val):
5264
val = val.strip('"')
65+
if not val:
66+
return None, val
67+
5368
sig = HMAC.new(self.secret, val[40:].encode('utf-8'), SHA1).hexdigest()
5469

5570
# Avoid timing attacks
5671
invalid_bits = 0
5772
input_sig = val[:40]
5873
if len(sig) != len(input_sig):
59-
return None, val
74+
return InvalidSignature, val
6075

6176
for a, b in zip(sig, input_sig):
6277
invalid_bits += a != b
6378

6479
if invalid_bits:
65-
return None, val
80+
return InvalidSignature, val
6681
else:
6782
return val[40:], val
6883

@@ -163,14 +178,24 @@ def __init__(self, request, id=None, invalidate_corrupt=False,
163178
cookieheader = request.get('cookie', '')
164179
if secret:
165180
try:
166-
self.cookie = SignedCookie(secret, input=cookieheader)
181+
self.cookie = SignedCookie(
182+
secret,
183+
input=cookieheader,
184+
)
167185
except http_cookies.CookieError:
168-
self.cookie = SignedCookie(secret, input=None)
186+
self.cookie = SignedCookie(
187+
secret,
188+
input=None,
189+
)
169190
else:
170191
self.cookie = SimpleCookie(input=cookieheader)
171192

172193
if not self.id and self.key in self.cookie:
173-
self.id = self.cookie[self.key].value
194+
cookie_data = self.cookie[self.key].value
195+
# Should we check invalidate_corrupt here?
196+
if cookie_data is InvalidSignature:
197+
cookie_data = None
198+
self.id = cookie_data
174199

175200
self.is_new = self.id is None
176201
if self.is_new:
@@ -180,7 +205,7 @@ def __init__(self, request, id=None, invalidate_corrupt=False,
180205
try:
181206
self.load()
182207
except Exception as e:
183-
if invalidate_corrupt:
208+
if self.invalidate_corrupt:
184209
util.warn(
185210
"Invalidating corrupt session %s; "
186211
"error was: %s. Set invalidate_corrupt=False "
@@ -302,31 +327,16 @@ def _decrypt_data(self, session_data):
302327
"""Bas64, decipher, then un-serialize the data for the session
303328
dict"""
304329
if self.encrypt_key:
305-
try:
306-
__, nonce_b64len = self.encrypt_nonce_size
307-
nonce = session_data[:nonce_b64len]
308-
encrypt_key = crypto.generateCryptoKeys(self.encrypt_key,
309-
self.validate_key + nonce, 1)
310-
payload = b64decode(session_data[nonce_b64len:])
311-
data = crypto.aesDecrypt(payload, encrypt_key)
312-
except:
313-
# As much as I hate a bare except, we get some insane errors
314-
# here that get tossed when crypto fails, so we raise the
315-
# 'right' exception
316-
if self.invalidate_corrupt:
317-
return None
318-
else:
319-
raise
330+
__, nonce_b64len = self.encrypt_nonce_size
331+
nonce = session_data[:nonce_b64len]
332+
encrypt_key = crypto.generateCryptoKeys(self.encrypt_key,
333+
self.validate_key + nonce, 1)
334+
payload = b64decode(session_data[nonce_b64len:])
335+
data = crypto.aesDecrypt(payload, encrypt_key)
320336
else:
321337
data = b64decode(session_data)
322338

323-
try:
324-
return self.serializer.loads(data)
325-
except:
326-
if self.invalidate_corrupt:
327-
return None
328-
else:
329-
raise
339+
return self.serializer.loads(data)
330340

331341
def _delete_cookie(self):
332342
self.request['set_cookie'] = True
@@ -526,12 +536,18 @@ class CookieSession(Session):
526536
:param encrypt_key: The key to use for the local session encryption, if not
527537
provided the session will not be encrypted.
528538
:param validate_key: The key used to sign the local encrypted session
539+
:param invalidate_corrupt: How to handle corrupt data when loading. When
540+
set to True, then corrupt data will be silently
541+
invalidated and a new session created,
542+
otherwise invalid data will cause an exception.
543+
:type invalidate_corrupt: bool
529544
"""
530545
def __init__(self, request, key='beaker.session.id', timeout=None,
531546
save_accessed_time=True, cookie_expires=True, cookie_domain=None,
532547
cookie_path='/', encrypt_key=None, validate_key=None, secure=False,
533548
httponly=False, data_serializer='pickle',
534-
encrypt_nonce_bits=DEFAULT_NONCE_BITS, **kwargs):
549+
encrypt_nonce_bits=DEFAULT_NONCE_BITS, invalidate_corrupt=False,
550+
**kwargs):
535551

536552
if not crypto.has_aes and encrypt_key:
537553
raise InvalidCryptoBackendError("No AES library is installed, can't generate "
@@ -550,7 +566,7 @@ def __init__(self, request, key='beaker.session.id', timeout=None,
550566
self.httponly = httponly
551567
self._domain = cookie_domain
552568
self._path = cookie_path
553-
569+
self.invalidate_corrupt = invalidate_corrupt
554570
self._set_serializer(data_serializer)
555571

556572
try:
@@ -565,9 +581,15 @@ def __init__(self, request, key='beaker.session.id', timeout=None,
565581
raise BeakerException("timeout requires save_accessed_time")
566582

567583
try:
568-
self.cookie = SignedCookie(validate_key, input=cookieheader)
584+
self.cookie = SignedCookie(
585+
validate_key,
586+
input=cookieheader,
587+
)
569588
except http_cookies.CookieError:
570-
self.cookie = SignedCookie(validate_key, input=None)
589+
self.cookie = SignedCookie(
590+
validate_key,
591+
input=None,
592+
)
571593

572594
self['_id'] = _session_id()
573595
self.is_new = True
@@ -577,10 +599,19 @@ def __init__(self, request, key='beaker.session.id', timeout=None,
577599
self.is_new = False
578600
try:
579601
cookie_data = self.cookie[self.key].value
602+
if cookie_data is InvalidSignature:
603+
raise BeakerException("Invalid signature")
580604
self.update(self._decrypt_data(cookie_data))
581605
self._path = self.get('_path', '/')
582-
except:
583-
pass
606+
except Exception as e:
607+
if self.invalidate_corrupt:
608+
util.warn(
609+
"Invalidating corrupt session %s; "
610+
"error was: %s. Set invalidate_corrupt=False "
611+
"to propagate this exception." % (self.id, e))
612+
self.invalidate()
613+
else:
614+
raise
584615

585616
if self.timeout is not None:
586617
now = time.time()

0 commit comments

Comments
 (0)