9
9
from beaker .exceptions import BeakerException , InvalidCryptoBackendError
10
10
from beaker .cookie import SimpleCookie
11
11
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 ()
13
25
14
26
15
27
try :
@@ -50,19 +62,22 @@ def __init__(self, secret, input=None):
50
62
51
63
def value_decode (self , val ):
52
64
val = val .strip ('"' )
65
+ if not val :
66
+ return None , val
67
+
53
68
sig = HMAC .new (self .secret , val [40 :].encode ('utf-8' ), SHA1 ).hexdigest ()
54
69
55
70
# Avoid timing attacks
56
71
invalid_bits = 0
57
72
input_sig = val [:40 ]
58
73
if len (sig ) != len (input_sig ):
59
- return None , val
74
+ return InvalidSignature , val
60
75
61
76
for a , b in zip (sig , input_sig ):
62
77
invalid_bits += a != b
63
78
64
79
if invalid_bits :
65
- return None , val
80
+ return InvalidSignature , val
66
81
else :
67
82
return val [40 :], val
68
83
@@ -163,14 +178,24 @@ def __init__(self, request, id=None, invalidate_corrupt=False,
163
178
cookieheader = request .get ('cookie' , '' )
164
179
if secret :
165
180
try :
166
- self .cookie = SignedCookie (secret , input = cookieheader )
181
+ self .cookie = SignedCookie (
182
+ secret ,
183
+ input = cookieheader ,
184
+ )
167
185
except http_cookies .CookieError :
168
- self .cookie = SignedCookie (secret , input = None )
186
+ self .cookie = SignedCookie (
187
+ secret ,
188
+ input = None ,
189
+ )
169
190
else :
170
191
self .cookie = SimpleCookie (input = cookieheader )
171
192
172
193
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
174
199
175
200
self .is_new = self .id is None
176
201
if self .is_new :
@@ -180,7 +205,7 @@ def __init__(self, request, id=None, invalidate_corrupt=False,
180
205
try :
181
206
self .load ()
182
207
except Exception as e :
183
- if invalidate_corrupt :
208
+ if self . invalidate_corrupt :
184
209
util .warn (
185
210
"Invalidating corrupt session %s; "
186
211
"error was: %s. Set invalidate_corrupt=False "
@@ -302,31 +327,16 @@ def _decrypt_data(self, session_data):
302
327
"""Bas64, decipher, then un-serialize the data for the session
303
328
dict"""
304
329
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 )
320
336
else :
321
337
data = b64decode (session_data )
322
338
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 )
330
340
331
341
def _delete_cookie (self ):
332
342
self .request ['set_cookie' ] = True
@@ -526,12 +536,18 @@ class CookieSession(Session):
526
536
:param encrypt_key: The key to use for the local session encryption, if not
527
537
provided the session will not be encrypted.
528
538
: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
529
544
"""
530
545
def __init__ (self , request , key = 'beaker.session.id' , timeout = None ,
531
546
save_accessed_time = True , cookie_expires = True , cookie_domain = None ,
532
547
cookie_path = '/' , encrypt_key = None , validate_key = None , secure = False ,
533
548
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 ):
535
551
536
552
if not crypto .has_aes and encrypt_key :
537
553
raise InvalidCryptoBackendError ("No AES library is installed, can't generate "
@@ -550,7 +566,7 @@ def __init__(self, request, key='beaker.session.id', timeout=None,
550
566
self .httponly = httponly
551
567
self ._domain = cookie_domain
552
568
self ._path = cookie_path
553
-
569
+ self . invalidate_corrupt = invalidate_corrupt
554
570
self ._set_serializer (data_serializer )
555
571
556
572
try :
@@ -565,9 +581,15 @@ def __init__(self, request, key='beaker.session.id', timeout=None,
565
581
raise BeakerException ("timeout requires save_accessed_time" )
566
582
567
583
try :
568
- self .cookie = SignedCookie (validate_key , input = cookieheader )
584
+ self .cookie = SignedCookie (
585
+ validate_key ,
586
+ input = cookieheader ,
587
+ )
569
588
except http_cookies .CookieError :
570
- self .cookie = SignedCookie (validate_key , input = None )
589
+ self .cookie = SignedCookie (
590
+ validate_key ,
591
+ input = None ,
592
+ )
571
593
572
594
self ['_id' ] = _session_id ()
573
595
self .is_new = True
@@ -577,10 +599,19 @@ def __init__(self, request, key='beaker.session.id', timeout=None,
577
599
self .is_new = False
578
600
try :
579
601
cookie_data = self .cookie [self .key ].value
602
+ if cookie_data is InvalidSignature :
603
+ raise BeakerException ("Invalid signature" )
580
604
self .update (self ._decrypt_data (cookie_data ))
581
605
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
584
615
585
616
if self .timeout is not None :
586
617
now = time .time ()
0 commit comments