# -*- coding: utf-8 -*- """ webapp2_extras.securecookie =========================== A serializer for signed cookies. :copyright: 2011 by tipfy.org. :license: Apache Sotware License, see LICENSE for details. """ import Cookie import hashlib import hmac import logging import time from webapp2_extras import json from webapp2_extras import security class SecureCookieSerializer(object): """Serializes and deserializes secure cookie values. Extracted from `Tornado`_ and modified. """ def __init__(self, secret_key): """Initiliazes the serializer/deserializer. :param secret_key: A random string to be used as the HMAC secret for the cookie signature. """ self.secret_key = secret_key def serialize(self, name, value): """Serializes a signed cookie value. :param name: Cookie name. :param value: Cookie value to be serialized. :returns: A serialized value ready to be stored in a cookie. """ timestamp = str(self._get_timestamp()) value = self._encode(value) signature = self._get_signature(name, value, timestamp) return '|'.join([value, timestamp, signature]) def deserialize(self, name, value, max_age=None): """Deserializes a signed cookie value. :param name: Cookie name. :param value: A cookie value to be deserialized. :param max_age: Maximum age in seconds for a valid cookie. If the cookie is older than this, returns None. :returns: The deserialized secure cookie, or None if it is not valid. """ if not value: return None # Unquote for old WebOb. value = Cookie._unquote(value) parts = value.split('|') if len(parts) != 3: return None signature = self._get_signature(name, parts[0], parts[1]) if not security.compare_hashes(parts[2], signature): logging.warning('Invalid cookie signature %r', value) return None if max_age is not None: if int(parts[1]) < self._get_timestamp() - max_age: logging.warning('Expired cookie %r', value) return None try: return self._decode(parts[0]) except Exception, e: logging.warning('Cookie value failed to be decoded: %r', parts[0]) return None def _encode(self, value): return json.b64encode(value) def _decode(self, value): return json.b64decode(value) def _get_timestamp(self): return int(time.time()) def _get_signature(self, *parts): """Generates an HMAC signature.""" signature = hmac.new(self.secret_key, digestmod=hashlib.sha1) signature.update('|'.join(parts)) return signature.hexdigest()