python tornado下的csrf怎么玩?

0、
什么是CSRF,就是跨站伪造请求。

恶意Web B让用户去访问Web A,比如让它删除自己的好友信息。
因为用户是带了A的正式的cookie过去的,所以Web A不知道用户的删除好友操作是坏人所为,就乖乖删除了好友。


1、
怎么防范-1?
用Referer字段咯。毕竟坏请求只能从Web B上发送出来,如果校验Referer字段的话,那么这个字段一定不是Web A从而防范成功。

2、
怎么防范-2?
用token,服务器给http请求返回一个token(可以放到cookie中给客户端浏览器),并要求Web A提交的请求必须带上token。
因为Web B中是绝对拿不到Web A的cookie的。。
所以把token放到Web A的cookie中,请求的时候带给服务器,由服务器去验证即可。

3、
具体tornado怎么搞?
先在app设置里面加一个xsrf_cookies = True。
app_settings = dict(
            template_path=os.path.join(os.path.dirname(__file__), "templates"),
            static_path=os.path.join(os.path.dirname(__file__), "static"),
            debug=False,
            login_url="/app_h5/backend/login",
            cookie_secret="61oETzKXQAGaYdkL5gEmGeJJFuYh7EQnp2XdTP1o/Vo=",
            compiled_template_cache=False,
            xsrf_cookies=True,
        )
然后每一个post请求都要带上cookies['_xsrf']上来。否则就给403.

4、
其实还是挺好奇tornado做了什么的。
为啥只要开启xsrf_cookies=True,然后就会生成一个token,然后浏览器带上这个token,服务器就能判断这个token有效。
猜测:这说明token是可以解码的!!!
看看源码怎么实现的:

4.0
先看看_xsrf长什么样子,类似: 2|b5a0dd74|b54547002d496a9844ca63fc4bda38ab|1508209078

4.1
看文档发现主要是RequestHandler.check_xsrf_cookie()在处理这个token的判断逻辑。
def check_xsrf_cookie(self):
    token = (self.get_argument("_xsrf", None) or
             self.request.headers.get("X-Xsrftoken") or
             self.request.headers.get("X-Csrftoken"))
    if not token:   #### 不带token就是403
        raise HTTPError(403, "'_xsrf' argument missing from POST")
    _, token, _ = self._decode_xsrf_token(token)  ##### 解析token,果然是可以解码的!!
    _, expected_token, _ = self._get_raw_xsrf_token() 
    if not token:   #### 解码失败就是403
        raise HTTPError(403, "'_xsrf' argument has invalid format")
    if not _time_independent_equals(utf8(token), utf8(expected_token)): #两个token是否相等
        raise HTTPError(403, "XSRF cookie does not match POST argument")
那么看看怎么个解码法:self._decode_xsrf_token()
    def _decode_xsrf_token(self, cookie):
        try:
            # _signed_value_version_re = re.compile(br"^([1-9][0-9]*)\|(.*)$”)
            # 能匹配类似 2112312|fhdaheajdshfjkasdf这样子的字符串。
            # 对应我们的_xsrf=2|b5a0dd74|b54547002d496a9844ca63fc4bda38ab|1508209078
                             # 那么m.group(1) == 2
            m = _signed_value_version_re.match(utf8(cookie))  ### 
            
            if m:
                version = int(m.group(1))
                if version == 2:
                    # _=2
                                                 # mask=b5a0dd74
                                                  # masked_token=b54547002d496a9844ca63fc4bda38ab
                                                 # timestamp = 1508209078
                    _, mask, masked_token, timestamp = cookie.split("|")

                    mask = binascii.a2b_hex(utf8(mask))  ### '\xb5\xa0\xddt'
                    token = _websocket_mask(
                        mask, binascii.a2b_hex(utf8(masked_token)))  
                    # '\xb5EG\x00-Ij\x98D\xcac\xfcK\xda8\xab’

                            
                    timestamp = int(timestamp)
                    return version, token, timestamp
                else:
                    # Treat unknown versions as not present instead of failing.
                    raise Exception("Unknown xsrf cookie version")
            else:
                version = 1
                try:
                    token = binascii.a2b_hex(utf8(cookie))
                except (binascii.Error, TypeError):
                    token = utf8(cookie)
                # We don't have a usable timestamp in older versions.
                timestamp = int(time.time())
                return (version, token, timestamp)
        except Exception:
            # Catch exceptions and return nothing instead of failing.
            gen_log.debug("Uncaught exception in _decode_xsrf_token",
                          exc_info=True)
            return None, None, None
因为version==2,进入一个分支:
                if version == 2:
                    ### _=2
                    ### mask=b5a0dd74
                    ### masked_token=b54547002d496a9844ca63fc4bda38ab
                    ### timestamp = 1508209078
                    _, mask, masked_token, timestamp = cookie.split("|")
                    
                    ### mask='\xb5\xa0\xddt'
                    mask = binascii.a2b_hex(utf8(mask))  

                    ### masked_token='\xb5EG\x00-Ij\x98D\xcac\xfcK\xda8\xab’
                    ### token='\x00\xe5\x9at\x98\xe9\xb7\xec\xf1j\xbe\x88\xfez\xe5\xdf'
                    token = _websocket_mask(
                        mask, binascii.a2b_hex(utf8(masked_token)))  
            
                    timestamp = int(timestamp)
                    return version, token, timestamp
然后就返回version,token,timestamp。

总结下:
1)、
我们用POST参数里面的_xsrf值,通过self._decode_xsrf_token()过程,得到version1,token1,timestamp1。
2)、
然后重复这个self._decode_xsrf_token()过程,只不过这次的_xsrf是来自cookie。
得到version2,token2,timestamp2。
3)、
判断两个version1==version2, token1==token2, timestamp1 == timestamp2,如果不等则403。

以上




  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值