前言
CSRF的防护主要思想就是保证请求是从正确的网站发出的。
CSRF攻击
攻击原理图如下,图片来自[1]
GET型攻击
get型攻击仅需伪造一次get请求即可,危害程度大
|
|
POST型攻击
|
|
CSRF检测
CSRFTester
源码阅读:入口类为CSRFTester,构造函数创建UI,main中创建proxy,Proxy(new Framework()).run()
CSRF防御
referer
网站B发出的请求referer为网站B,只需在后端判断referer是否同站即可防御CSRF。这样相当于把安全寄托于第三方,因为浏览器都不支持js对referer头的修改[4]&[5],有的文章[6]说可以使用axios进行referer绕过,但是我测试后并不行。
Refused to set unsafe header "referer"
js不允许修改referer头,但是hackbar可以添加任意头,是如何实现的呢。
Hackbar源码阅读
文件系统中搜索chrome插件id就很容易找到解包后源码的位置,可以发现连Hackbar都是大名鼎鼎的vue写的。
搜索referer可以在index.js中看到commonRequestHeaders的函数列出常见的request头
发现主要逻辑如下,就是通过v-model动态绑定vue对象data数据中headers中的值
|
|
headers只是存储数据,执行时调用chrome.runtime.connect()
这个接口,他的第一个参数是extension id。
The ID of the extension or app to connect to. If omitted, a connection will be attempted with your own extension. Required if sending messages from a web page for web messaging.[7]
|
|
到这里可以知道了,hackbar使用的是浏览器提供的扩展程序接口,所以可以修改header
csrf token
也正是由于CSRF产生的原理,csrf_token不能放在cookie中,否则达不到防御CSRF的效果;csrf_token也无需每次都不一样(每次不同是防止重放攻击,若仅考虑CSRF则无需不同)。
一般form表单加个hidden属性的input标签来存放。由于csrf_token存放在网站A的DOM中,这样网站B就无法获取token,即使伪造使得流量器访问网站A,A后端检测到没有token也不会允许请求。
至于hidden这个属性,个人认为即使是明文也不影响对CSRF的防御,主要是减少用户感知。
|
|
csrf_token
的设计:基于攻击者无法得知cookie,最简单的可直接将sessionid等作为token(md5值等同理);或者直接生成随机字符串
添加http request头
添加额外的 HTTP 头属性,并把 token 值放入其中,比如叫 csrftoken
从底层到高层XMLHttpRequest、Ajax、 Axios等都支持手动添加header头,较方便。个人认为不存在[1]中描述的局限性较大的问题。
添加验证机制
在请求数据提交前,需填写验证码信息,以增加对用户来源的有效认证,防止恶意未授权的操作产生。
Flask CSRF Strategy
如果没有指定允许POST,提交表单的时候会提示Method Not Allowed
|
|
如果允许了POST,再次尝试提交表单会Bad Request,这也是CSRF攻击未携带token的场景
|
|
如果template中添加token,在访问网站的时候请求到token,然后提交表单的时候将token也post出去,这样就能正确响应。尝试手动修改DOM中的token再次post也是bad request。
|
|
源码分析
https://github.com/wtforms/flask-wtf/blob/main/src/flask_wtf/csrf.py
CSRFProtect类中init_app初始先对app进行一些参数的配置并用generate_csrf方法获取随机token并保存到session中;然后对app添加@app.before_request的hook,请求来时先检测app的参数配置,不符合直接return,然后进到protect方法,其中先validate_csrf(self._get_csrf_token())检测token是否与session中一致再使用same_origin方法检测referrer是否同源,如果是最后再表示请求是CSRF valid
参考文献
[1] https://www.cnblogs.com/54chensongxia/p/11693666.html [csrf]
[2] https://www.cnblogs.com/leijiangtao/p/11770647.html [flask csrf]
[3] http://luckyzmj.cn/posts/a1b686d3.html#! [CSRFTester]
[4] https://blog.csdn.net/neal1991/article/details/114609935 [js referer]
[5] https://developer.mozilla.org/en-US/docs/Glossary/Forbidden_header_name [forbidden header name]
[6] https://blog.csdn.net/Forever201295/article/details/80237134 [axios referer forgery]
[7] https://developer.chrome.com/docs/extensions/reference/runtime/#method-connect [runtime.connect]
[8] https://uknowsec.cn/posts/notes/CSRF%E6%BC%8F%E6%B4%9E%E7%9A%84%E5%88%A9%E7%94%A8%E4%B8%8E%E5%AD%A6%E4%B9%A0.html [attack csrftester]
[9] https://github.com/wtforms/flask-wtf/blob/main/src/flask_wtf/csrf.py [flask csrf source code]