You know some birds are not meant to be caged, their feathers are just too bright. ——Shawshank Redemption
何为CSRF
- CSRF = 跨站点+请求伪造
- 在用户不知情的情况下进行网络请求
- hacker利用用户身份,操作用户账户
XSS在构造GET和POST请求时,和CSRF有点像,但一定注意原理是不同的,例如,删除sohu博客的命令是http://blog.sohu.com/manage/entry.do?m=delete&id=156713012
hacker在自己的域下构造页面http:/ /www.a.com/csrf.html
其中,csrf.html的内容为
1 | <img src="http://blog.sohu.com/manage/entry.do?m=delete&id=156713012" /> |
然后hacker诱导用户访问这个页面。注意,这个页面可以与blog是可以不同域的。如果是对该blog使用XSS攻击,那么这个页面和blog是一定要同源的,用户访问该页面时,自己的html页面被注入了一张“看不到的照片”,经过htmlparse解析之后,攻击成功(如果存在XSS漏洞的话);如果要对blog使用CSRF攻击,这个页面不要求和blog同源,但要求用户之前访问过blog(用户有blog给的cookie),用户访问这个页面时,页面的浏览器就会给blog服务器发一个GET请求,攻击成功。
跨站点
跨站点就是要克服同源策略。
cookie
- 一个域(a.com)如果要加载另一个域(b.com)的资源,某些浏览器是会阻止本地cookie的发送的(如果浏览器不阻止的话,直接就克服了同源策略)
- 阻止了怎么办呢?因为session_cookie是保存在浏览器进程内存中的,所以session_cookie是会发送的,所以我们只需要构造一个场景,也就是先让用户访问一下b.com,这时候浏览器进程内存里就有了对应的session_cookie,然后再进行CSRF
P3P头的副作用(IE特有)
- 如果服务器给浏览器返回的HTTP头中包含P3P头的话,在某种程度上,就允许服务器把本地cookie发送过去
场景:
http://www.b.com/test.html
的内容为
1 | <iframe width=300 height=300 src="http://www.a.com/test.php" ></iframe> |
http://www.a.com/test.php
内容为:
1 | <?php |
由于同源策略,上面的例子是不会成功的,无论是session_cookie还是本地cookie都一样。
但如果修改一下http://www.a.com/test.php
内容
1 | <?php |
就会成功了。
P3P只需要网站设置一次即可,不需要每次都设置
请求伪造
Get
- 直接在url里面改一改参数,可能就伪造出来了
<img> <iframe> <script>
等标签只能发起一次get请求,无法发起post请求
Post
- 对于很多网站来说,一些重要的操作并未严格区分Get和Post(例如在PHP中,如果用的是
$_REQUEST
,没用$_POST
获取变量的话,就会出现这样的问题),hacker可以尝试用Get来请求表单的提交地址 - 服务器对这个get请求做了限制,说明服务器严格区分了Get和Post,这个时候就只能自己来构造Post请求
例如,a.com
表单内容:
1 | <form action="/register" id="register" method="post" > |
尝试构造GET请求:http://host/register?username=test&password=passwd
如果被限制了,就只能自己构造表单。最简单的方法是在一个页面构造好form表单,然后使用JavaScript自动提交这个表单,例如,hacker在www.b.com/test.html
中写入:
1 | <form action="http://www.a.com/register" id="register" method="post" > |
然后就是诱导用户点进恶意网站,通过跨站点把表单发出去,当然这个请求也会带上用户的cookie,例如可以让用户在不知不觉中又写了一篇blog
CSRF的防御
验证码
- CSRF是在用户不知情的情况下,构造网络请求,验证码会强迫用户与应用交互
- 但很多网站为了保证用户体验,很多操作不会设置验证码,所以这只能作为一个辅助手段
Referer Check
- referer是html请求头,可以用于“防止图片盗链”
- 比如论坛发帖,只有有限的几个页面可以进行发帖的操作,所以在提交“发帖”表单时,如果referee的值不在这几个页面之中,大概率就是CSRF
- 但这只是充分条件,并不是必要的
- 而且服务器并非什么时候都可以取到referee,很多用户为了隐私保护,限制了referer的发送,在某些特定的情况下,浏览器也不会发送referee(例如从https跳转到http)
Anti CSRF Token
CSRF的本质
- CSRF = 跨站点 + 请求伪造
- 二者缺一不可
- 请求伪造的本质:hacker猜测所有参数
- 防御思路:创造一个不可预测的参数
token的诞生
- 在原本的url后面加一个token参数,token必须足够随机,必须使用足够安全的随机数生成算法,或者采用真随机数生成器。
- 实际应用中,token存储在服务端的session或者cookie中(笔者认为最好放在session中,以保障安全)
- 这样hacker就无法构造一个完整的URL
- token需要同时放在表单(或者URL),和session(或者cookie)中,在提交表单后,服务器只需要比较一下2个token是否一致即可,不一致就有可能发生了CSRF
token的使用原则
- 必须足够随机
- 为了方便,允许一个用户在有效生命周期内,在token被消耗掉之前,一直用一个token。一旦表单被提交,token被消耗,就要换一个
- 如果token保存在cookie中,浏览器打开好几个页面,同时操作,其中一个页面消耗掉旧token后,又产生了新的token,但在另外几个页面中保存的还是旧token,他们再被提交时就会发生token错误。所有可以考虑生成多个有效的token,来解决多页面共存的场景(也就是说,表单中的token,只要在是这多个token之一,就是正确的)
- token如果在url中会很危险,比如,在页面中有一张hacker可以指定地址的图片,hacker只需要把地址选在自己的服务器,页面的url就会作为referer发送到hacker的服务器。所有在使用token时,尽量放在表单中,尽量用post,而不是get
- 如果网站存在XSS漏洞,token就会变得无效,因为XSS可以模拟用户浏览器做任何操作,自然包括读出你的token
本文链接: https://bano247.com/2021/11/03/跨站点请求伪造(CSRF)/
版权声明: 本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可。转载请注明出处!