1# -*- coding: utf-8 -*- 2""" 3 webapp2_extras.securecookie 4 =========================== 5 6 A serializer for signed cookies. 7 8 :copyright: 2011 by tipfy.org. 9 :license: Apache Sotware License, see LICENSE for details. 10""" 11import Cookie 12import hashlib 13import hmac 14import logging 15import time 16 17from webapp2_extras import json 18from webapp2_extras import security 19 20 21class SecureCookieSerializer(object): 22 """Serializes and deserializes secure cookie values. 23 24 Extracted from `Tornado`_ and modified. 25 """ 26 27 def __init__(self, secret_key): 28 """Initiliazes the serializer/deserializer. 29 30 :param secret_key: 31 A random string to be used as the HMAC secret for the cookie 32 signature. 33 """ 34 self.secret_key = secret_key 35 36 def serialize(self, name, value): 37 """Serializes a signed cookie value. 38 39 :param name: 40 Cookie name. 41 :param value: 42 Cookie value to be serialized. 43 :returns: 44 A serialized value ready to be stored in a cookie. 45 """ 46 timestamp = str(self._get_timestamp()) 47 value = self._encode(value) 48 signature = self._get_signature(name, value, timestamp) 49 return '|'.join([value, timestamp, signature]) 50 51 def deserialize(self, name, value, max_age=None): 52 """Deserializes a signed cookie value. 53 54 :param name: 55 Cookie name. 56 :param value: 57 A cookie value to be deserialized. 58 :param max_age: 59 Maximum age in seconds for a valid cookie. If the cookie is older 60 than this, returns None. 61 :returns: 62 The deserialized secure cookie, or None if it is not valid. 63 """ 64 if not value: 65 return None 66 67 # Unquote for old WebOb. 68 value = Cookie._unquote(value) 69 70 parts = value.split('|') 71 if len(parts) != 3: 72 return None 73 74 signature = self._get_signature(name, parts[0], parts[1]) 75 76 if not security.compare_hashes(parts[2], signature): 77 logging.warning('Invalid cookie signature %r', value) 78 return None 79 80 if max_age is not None: 81 if int(parts[1]) < self._get_timestamp() - max_age: 82 logging.warning('Expired cookie %r', value) 83 return None 84 85 try: 86 return self._decode(parts[0]) 87 except Exception, e: 88 logging.warning('Cookie value failed to be decoded: %r', parts[0]) 89 return None 90 91 def _encode(self, value): 92 return json.b64encode(value) 93 94 def _decode(self, value): 95 return json.b64decode(value) 96 97 def _get_timestamp(self): 98 return int(time.time()) 99 100 def _get_signature(self, *parts): 101 """Generates an HMAC signature.""" 102 signature = hmac.new(self.secret_key, digestmod=hashlib.sha1) 103 signature.update('|'.join(parts)) 104 return signature.hexdigest() 105