大家好,又见面了,我是你们的朋友全栈君。
Torando源码解析之XSRF防护的实现
Tornado开启XSRF防护的方法
http://tornado-zh.readthedocs.io/zh/latest/guide/security.html
核心代码如下
Tornado开启xsrf_cookies
settings = {
"cookie_secret": "__TODO:_GENERATE_YOUR_OWN_RANDOM_VALUE_HERE__",
"login_url": "/login",
"xsrf_cookies": True,
}
application = tornado.web.Application([
(r"/", MainHandler),
(r"/login", LoginHandler),
], **settings)
HTML页面添加相关的代码
<form action="/new_message" method="post"> {% module xsrf_form_html() %} ...... </form>
源码解析
在上述的代码中,很明显可以看出,所谓开启xsrf防护,其实就是在post提交的数据里面带上 {% module xsrf_form_html() %} 这段代码所包含的东西
xsrf_form_html()是什么
找到tornado的目录,然后再该目录下通过linux命令找到xsrf_form_html
grep xsrf_form_html -r *
查看grep结果,找到相关代码如下
def xsrf_form_html(self):
return '<input type="hidden" name="_xsrf" value="' + \
escape.xhtml_escape(self.xsrf_token) + '"/>'
发现所谓的 {% module xsrf_form_html() %}其实就是隐藏的一个
self.xsrf_token是什么
同样的方法找到xsrf_token
@property
def xsrf_token(self):
if not hasattr(self, "_xsrf_token"):
version, token, timestamp = self._get_raw_xsrf_token()
output_version = self.settings.get("xsrf_cookie_version", 2)
cookie_kwargs = self.settings.get("xsrf_cookie_kwargs", {})
if output_version == 1:
self._xsrf_token = binascii.b2a_hex(token)
elif output_version == 2:
mask = os.urandom(4)
self._xsrf_token = b"|".join([
b"2",
binascii.b2a_hex(mask),
binascii.b2a_hex(_websocket_mask(mask, token)),
utf8(str(int(timestamp)))])
else:
raise ValueError("unknown xsrf cookie version %d",
output_version)
if version is None:
expires_days = 30 if self.current_user else None
self.set_cookie("_xsrf", self._xsrf_token,
expires_days=expires_days,
**cookie_kwargs)
return self._xsrf_token
其中,@property 是将该类函数设置成属性访问的装饰器
像第一次访问的时候,_xsrf_token这个值肯定是没有的
由代码可以看出token是其实就是self.xsrf_token的关键
def _get_raw_xsrf_token(self):
if not hasattr(self, '_raw_xsrf_token'):
cookie = self.get_cookie("_xsrf")
if cookie:
version, token, timestamp = self._decode_xsrf_token(cookie)
else:
version, token, timestamp = None, None, None
if token is None:
version = None
token = os.urandom(16)
timestamp = time.time()
self._raw_xsrf_token = (version, token, timestamp)
return self._raw_xsrf_token
这里其实就很明显可以看出
如果是第一次生成token,那么
token = os.urandom(16)
os.urandom(n) 是随机生成n个字节的函数
所以,token就是一个随机的16字节的串
如果不是第一次。那么会从cookie里面去获取token,并将这个token重新传回给前端
def _decode_xsrf_token(self, cookie):
try:
m = _signed_value_version_re.match(utf8(cookie))
if m:
version = int(m.group(1))
if version == 2:
_, mask, masked_token, timestamp = cookie.split("|")
mask = binascii.a2b_hex(utf8(mask))
token = _websocket_mask(
mask, binascii.a2b_hex(utf8(masked_token)))
timestamp = int(timestamp)
return version, token, timestamp
else:
raise Exception("Unknown xsrf cookie version")
else:
version = 1
try:
token = binascii.a2b_hex(utf8(cookie))
except (binascii.Error, TypeError):
token = utf8(cookie)
timestamp = int(time.time())
return (version, token, timestamp)
except Exception:
gen_log.debug("Uncaught exception in _decode_xsrf_token",
exc_info=True)
return None, None, None
这个与前面的xsrf_token 函数相对应,因为在传给前端的时候token进行了”encode”,所以获取的时候需要“decode”下。
当然这里所谓的”encode”和”decode”,其实仅仅是将版本号和当前的时间戳以某种规则加在了返回的token两边。
发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/144705.html原文链接:https://javaforall.cn
【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛
【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...