August 2014 Blog Posts

Tornado中的secure cookie

MitchellChu 2014-08-13 其他技术 编程语言

在开始用的web框架Tornado的时候,以为Tornado中的get_secure_cookieset_secure_cookie是用来设置加密后的Cookie信息的,但今天看了源代码之后,发现情况并非如想象的那样,secure_cookie在Tornado中的作用是对Cookie值进行签名而已。新版本(v4.0)和老版本(1.0)相较而言,虽然增加了第二个版本的secure_cookie,但功能仍然是一样。

1.0中,使用的是SHA1进行签名,而在Tornado 4.0中的新版本中使用的是SHA256进行签名,同时输出的Cookie格式有差异——开始第一个数字是版本号(secure cookie使用的version,而非Tornado的版本)。

新版本的set_secure_cookie设置的相关源码如下:

## Tornado 4.0中web.py的部分源码
## set_secure_cookie相关的源码

MIN_SUPPORTED_SIGNED_VALUE_VERSION = 1
MAX_SUPPORTED_SIGNED_VALUE_VERSION = 2
DEFAULT_SIGNED_VALUE_VERSION = 2
DEFAULT_SIGNED_VALUE_MIN_VERSION = 1

class RequestHandler(object):
    def set_cookie(self, name, value, domain=None, expires=None, path="/",
                   expires_days=None, **kwargs):
        name = escape.native_str(name)
        value = escape.native_str(value)
        if re.search(r"[\x00-\x20]", name + value):
            raise ValueError("Invalid cookie %r: %r" % (name, value))
        if not hasattr(self, "_new_cookie"):
            self._new_cookie = Cookie.SimpleCookie()
        if name in self._new_cookie:
            del self._new_cookie[name]
        self._new_cookie[name] = value
        morsel = self._new_cookie[name]
        if domain:
            morsel["domain"] = domain
        if expires_days is not None and not expires:
            expires = datetime.datetime.utcnow() + datetime.timedelta(
                days=expires_days)
        if expires:
            morsel["expires"] = httputil.format_timestamp(expires)
        if path:
            morsel["path"] = path
        for k, v in kwargs.items():
            if k == 'max_age':
                k = 'max-age'
            morsel[k] = v

    def set_secure_cookie(self, name, value, expires_days=30, version=None,
                          **kwargs):
        self.set_cookie(name, self.create_signed_value(name, value,
                                                       version=version),
                        expires_days=expires_days, **kwargs)

    def create_signed_value(self, name, value, version=None):
        self.require_setting("cookie_secret", "secure cookies")
        return create_signed_value(self.application.settings["cookie_secret"],
                                   name, value, version=version)

def create_signed_value(secret, name, value, version=None, clock=None):
    if version is None:
        version = DEFAULT_SIGNED_VALUE_VERSION
    if clock is None:
        clock = time.time
    timestamp = utf8(str(int(clock())))
    value = base64.b64encode(utf8(value))
    if version == 1:
        signature = _create_signature_v1(secret, name, value, timestamp)
        value = b"|".join([value, timestamp, signature])
        return value
    elif version == 2:
        def format_field(s):
            return utf8("%d:" % len(s)) + utf8(s)
        to_sign = b"|".join([
            b"2|1:0",
            format_field(timestamp),
            format_field(name),
            format_field(value),
            b''])
        signature = _create_signature_v2(secret, to_sign)
        return to_sign + signature
    else:
        raise ValueError("Unsupported version %d" % version)


def _create_signature_v1(secret, *parts):
    hash = hmac.new(utf8(secret), digestmod=hashlib.sha1)
    for part in parts:
        hash.update(utf8(part))
    return utf8(hash.hexdigest())


def _create_signature_v2(secret, s):
    hash = hmac.new(utf8(secret), digestmod=hashlib.sha256)
    hash.update(utf8(s))
    return utf8(hash.hexdigest())

 调用的关系如下:

-------------+--------------------------+--------------------------------+-----------------------------------+---------------------------------+--------------------------
             |                          |                                |                                   |                                 |
             |                          |                                |                                   |                                 |
             |                          |                                |                                   |                                 |
             |                          |                                |                                   |                                 |
 +-----------+---------+                |                                |                                   |                                 |
 |                     |                |                                |                                   |                                 |
 |  set_secure_cookie  |                |                                |                                   |                                 |
 |                     |                |                                |                                   |                                 |
 +-----------+---------+                |                                |                                   |                                 |
             |                          |                                |                                   |                                 |
             |              +-----------+-------------+                  |                                   |                                 |
             |     invoke   |                         |                  |                                   |                                 |
             +------------->|   create_signed_value   |                  |                                   |                                 |
             |              |                         |                  |                                   |                                 |
             |              +-----------+-------------+                  |                                   |                                 |
             |                          |                                |                                   |                                 |
             |                          |                 +--------------+-------------+                     |                                 |
             |                          |     invoke      |                            |                     |                                 |
             |                          +---------------->| create_signed_value(global)|                     |                                 |
             |                          |                 |                            |                     |                                 |
             |                          |                 +--------------+-------------+                     |                                 |
             |                          |                                |                                   |                                 |
             |                          |                                |                 +-----------------+-------------+                   |
             |                          |                                | invoke          |                               |                   |
             |                          |                                +---------------->| _create_signature_v2(global)  |                   |
             |                          |                                |                 |                               |                   |
             |                          |                                |                 +-----------------+-------------+                   |
             |                          |                                |   return signature string (sha256)|                                 |
             |                          | return source and signature    |<----------------------------------+                                 |
             |          result          |<-------------------------------+                                   |                                 |
             |<-------------------------|           string               |                                   |                                 |
             |                          |                                |                                   |                                 |
             |                          |                                |                                   |                                 |
             |                          |                                |                                   |                  +--------------+----------+
             |                          |                                |                                   |                  |                         |
             +-------------------------- -------------------------------- ----------------------------------- ----------------->|       set_cookie        |
             |                          |                invoke set_cookie to set secure_cookie              |                  |                         |
             |                          |                                |                                   |                  +--------------+----------+
             |                          |                                |                                   |                                 |
             |                          |                                |                                   |                                 |
             |                          |                                |                                   |                                 |
             |                          |                                |                                   |                                 |
             |                          |                                |                                   |                                 |
             |                          |                                |                                   |                                 |
             |                          |                                |                                   |                                 |

需要注意,在RequestHandler类中的create_signed_value方法调用的是web.py中的模块方法create_signed_value。在获取到返回结果之后,set_secure_cookie调用set_cookie,将放入cookie内。

版本2的secure cookie和版本1的签名区别就在_create_signature_v2_create_signature_v1上。

关于博主

  一枚成分复杂的网络IT分子,属于互联网行业分类中的杂牌军。