Torando源码解析之XSRF防护的实现

2022-08-26 20:11:48 浏览数 (1)

大家好,又见面了,我是你们的朋友全栈君。

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

0 人点赞