大家好,又见面了,我是你们的朋友全栈君。
Torando源码解析之XSRF防护的实现
- Torando源码解析之XSRF防护的实现
- Tornado开启XSRF防护的方法
- 源码解析
- xsrf_form_html()是什么
- self.xsrf_token是什么
Tornado开启XSRF防护的方法
http://tornado-zh.readthedocs.io/zh/latest/guide/security.html
核心代码如下 Tornado开启xsrf_cookies
代码语言:javascript复制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页面添加相关的代码
代码语言:javascript复制<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
代码语言:javascript复制grep xsrf_form_html -r *
查看grep结果,找到相关代码如下
代码语言:javascript复制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
代码语言:javascript复制@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的关键
代码语言:javascript复制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,那么
代码语言:javascript复制token = os.urandom(16)
os.urandom(n) 是随机生成n个字节的函数 所以,token就是一个随机的16字节的串
如果不是第一次。那么会从cookie里面去获取token,并将这个token重新传回给前端
代码语言:javascript复制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两边。
发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/144705.html原文链接:https://javaforall.cn