本文和大家分享的主要是tornado中cookie
验证机制相关内容,一起来看看吧,希望对大家
学习tornado有所帮助。
处理过程简单来说就是验证密码之后服务器端(tornado)
返回带有
cookie
信息的
Set-Cookie header
给客户端
,
之后客户端发起请求时会把此
cookie
放入
Cookie header
中发给服务器端。
tornado 设置 cookie
首先是对 cookie
的变量进行设置
, Morsel
是含有几个特殊
key
的类似于
dict
的对象
def
set_cookie(self, name, value, domain=None, expires=None, path="/", expires_days=None):
if
not hasattr(self, "_new_cookie"):
self._new_cookie = Cookie.SimpleCookie()
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
然后将 cookie
的
header flush
给客户端
def
flush(self, include_footers=False, callback=None):
...
if hasattr(self, "_new_cookie"):
for cookie
in self._new_cookie.values():
self.add_header("Set-Cookie", cookie.OutputString(
None))
...
return self.request.connection.write_headers(
start_line, self._headers, chunk, callback=callback)
...
torando 读取 cookie 并验证
tornado
从浏览器那获取
cookie
则特别简单,直接取出
header
中
Cookie
字段的内容
,
然后解析一下
def
cookies(self):
if
not hasattr(self, "_cookies"):
self._cookies = Cookie.SimpleCookie()
if "Cookie"
in self.headers:
try:
parsed = parse_cookie(self.headers["Cookie"])
except Exception:
pass
else:
for k, v
in parsed.items():
try:
self._cookies[k] = v
except Exception:
pass
return self._cookies
以上代码就是 cookie
在
server
和浏览器中传递的过程
.
当然这只是简单的传递
,
很容易找到规律并进行暴力破解进行提权攻击
.
tornado
提供了加密的
cookie, cookie
的传递还是上述代码
,
唯一的不同是在服务端对
cookie
的
value
进行了加密
,
这样用户即使知道其他用户的名字
,
也无法在短时间内构造出一条正确的
cookie
tornado
在服务端需要自己定义一个
secret key.
一个加密的
cookie
的
value
由
(value, timestamp, signature)
三元组组成
,
一般
value
可以通过加密算法构造
,
而
timestamp
则直接可以从现有的
cookie
里面直接取
,
所以最重要的是
signature
的构造
,
由于用户不知道
secret.
所以用户无法通过算法构造出
signature,
只能窃取或者通过暴力破解
.
而 服务端则能够通过
cookie
确认
value
是正常的
value
且能够取出
value
里包含的信息
加密代码
def
create_signed_value(secret, name, value):
clock = time.time
timestamp = utf8(str(int(clock())))
value = base64.b64encode(utf8(value))
signature = _create_signature_v1(secret, name, value, timestamp)
value = b"|".join([value, timestamp, signature])
return value
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
_decode_signed_value_v1(secret, name, value, max_age_days, clock):
parts = utf8(value).split(b"|")
signature = _create_signature_v1(secret, name, parts[0], parts[1])
if
not _time_independent_equals(parts[2], signature):
return
None
clock = time.time
timestamp = int(parts[1])
if timestamp < clock() - max_age_days * 86400:
gen_log.warning("Expired cookie %r", value)
return
None
return base64.b64decode(parts[0])
def
_time_independent_equals(a, b):
for x, y
in zip(a, b):
result |= ord(x) ^ ord(y)
return result == 0
总结
函数 _time_independent_equals
是很讲究的。 它总是花费同样的时间去比较用户的输入和你计算的结果。比如用户想要暴力构造一些
session,
如果比较函数花费的时间和
signature
前面
n
字节是否正确正
(
或者负
)
相关。那么变更
signature,
通过大量查看延时
,
理论上是能把
signature
暴力破解出来的
,
而这个
_time_independent_equals
可以防止这种攻击。
另外, tornado
这种校验
cookie
的方式能够天然解决
cookie
一致性的问题,可以方面的进行水平扩展。
来源: nosa.me