Beispiel #1
0
    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.
        """
        name = webapp3._to_utf8(name)
        timestamp = webapp3._to_utf8(str(self._get_timestamp()))
        value = self._encode(value)
        signature = self._get_signature(name, value, timestamp)
        return b'|'.join([value, timestamp, signature])
Beispiel #2
0
    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 = webapp3._to_utf8(secret_key)
Beispiel #3
0
    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

        name = webapp3._to_utf8(name)
        value = webapp3._to_utf8(value)

        # Unquote for old WebOb.
        value = http_cookies._unquote(value)

        parts = value.split(b'|')
        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:
            logging.warning('Cookie value failed to be decoded: %r', parts[0])
            return None
Beispiel #4
0
    def __init__(self, user_id, secret, current_time=None):
        """Initializes the XSRFToken object.

        :param user_id:
            A string representing the user that the token will be valid for.
        :param secret:
            A string containing a secret key that will be used to seed the
            hash used by the :class:`XSRFToken`.
        :param current_time:
            An int representing the number of seconds since the epoch. Will be
            used by `verify_token_string` to check for token expiry. If `None`
            then the current time will be used.
        """
        self.user_id = webapp3._to_utf8(user_id)
        self.secret = webapp3._to_utf8(secret)
        if current_time is None:
            self.current_time = int(time.time())
        else:
            self.current_time = int(current_time)

        self.current_time = webapp3._to_utf8(str(self.current_time))
Beispiel #5
0
def hash_password(password, method, salt=None, pepper=None):
    """Hashes a password.

    Supports plaintext without salt, unsalted and salted passwords. In case
    salted passwords are used hmac is used.

    :param password:
        The password to be hashed.
    :param method:
        A method from ``hashlib``, e.g., `sha1` or `md5`, or `plain`.
    :param salt:
        A random salt string.
    :param pepper:
        A secret constant stored in the application code.
    :returns:
        A hashed password.

    This function was ported and adapted from `Werkzeug`_.
    """
    if method == 'plain':
        return password

    password = webapp3._to_utf8(password)

    method = getattr(hashlib, method, None)
    if not method:
        return None

    if salt:
        h = hmac.new(webapp3._to_utf8(salt), password, method)
    else:
        h = method(password)

    if pepper:
        h = hmac.new(webapp3._to_utf8(pepper), webapp3._to_utf8(h.hexdigest()),
                     method)

    return h.hexdigest()
Beispiel #6
0
    def generate_token_string(self, action=None):
        """Generate a hash of the given token contents that can be verified.

        :param action:
            A string representing the action that the generated hash is valid
            for. This string is usually a URL.
        :returns:
            A string containing the hash contents of the given `action` and the
            contents of the `XSRFToken`. Can be verified with
            `verify_token_string`. The string is base64 encoded so it is safe
            to use in HTML forms without escaping.
        """
        digest_maker = self._digest_maker()
        digest_maker.update(self.user_id)
        digest_maker.update(self._DELIMITER)
        if action:
            digest_maker.update(webapp3._to_utf8(action))
            digest_maker.update(self._DELIMITER)

        digest_maker.update(self.current_time)
        return base64.urlsafe_b64encode(
            self._DELIMITER.join([
                webapp3._to_utf8(digest_maker.hexdigest()), self.current_time
            ]))
Beispiel #7
0
    def verify_token_string(self,
                            token_string,
                            action=None,
                            timeout=None,
                            current_time=None):
        """Generate a hash of the given token contents that can be verified.

        :param token_string:
            A string containing the hashed token (generated by
            `generate_token_string`).
        :param action:
            A string containing the action that is being verified.
        :param timeout:
            An int or float representing the number of seconds that the token
            is valid for. If None then tokens are valid forever.
        :current_time:
            An int representing the number of seconds since the epoch. Will be
            used by to check for token expiry if `timeout` is set. If `None`
            then the current time will be used.
        :raises:
            XSRFTokenMalformed if the given token_string cannot be parsed.
            XSRFTokenExpiredException if the given token string is expired.
            XSRFTokenInvalid if the given token string does not match the
            contents of the `XSRFToken`.
        """
        import binascii

        try:
            decoded_token_string = base64.urlsafe_b64decode(
                webapp3._to_utf8(token_string))
        except (TypeError, binascii.Error):
            raise XSRFTokenMalformed()

        split_token = decoded_token_string.split(self._DELIMITER)
        if len(split_token) != 2:
            raise XSRFTokenMalformed()

        try:
            token_time = int(split_token[1])
        except ValueError:
            raise XSRFTokenMalformed()

        if timeout is not None:
            if current_time is None:
                current_time = time.time()
            # If an attacker modifies the plain text time then
            # it will not match the hashed time so this check is sufficient.
            if (token_time + timeout) < current_time:
                raise XSRFTokenExpiredException()

        expected_token = XSRFToken(self.user_id, self.secret, token_time)
        expected_token_string = expected_token.generate_token_string(action)

        if len(expected_token_string) != len(token_string):
            raise XSRFTokenInvalid()

        # Compare the two strings in constant time to prevent timing attacks.
        different = 0
        for a, b in zip(webapp3._to_basestring(token_string),
                        webapp3._to_basestring(expected_token_string)):
            different |= ord(a) ^ ord(b)
        if different:
            raise XSRFTokenInvalid()
Beispiel #8
0
 def _get_signature(self, *parts):
     """Generates an HMAC signature."""
     signature = hmac.new(self.secret_key, digestmod=hashlib.sha1)
     signature.update(b'|'.join(parts))
     return webapp3._to_utf8(signature.hexdigest())