CSRF 漏洞
相关课程的线上实验版本已投递到实验楼,实验楼在线教育平台提供非常优秀的在线实验环境,建议有兴趣的朋友可以看看:点击前往
一、文章介绍
CSRF(Cross-site request forgery)跨站请求伪造,也被称为 One Click Attack 或者 Session Riding ,通常缩写为 CSRF 或者 XSRF ,是一种对网站的恶意利用。尽管听起来像跨站脚本(XSS),但它与 XSS 非常不同, XSS 利用站点内的信任用户,而 CSRF 则通过伪装来自受信任用户的请求来利用受信任的网站。与 XSS 攻击相比, CSRF 攻击往往不大流行(因此对其进行防范的资源也相当稀少)和难以防范,所以被认为比 XSS 更具危险性。
本文要解决以下几个问题:
-
什么是 CSRF 漏洞?
-
CSRF 和 XSS 的区别?
-
如何防范 CSRF 漏洞?
二、漏洞简介
跨站请求攻击,简单地说,是攻击者通过一些技术手段欺骗用户的浏览器去访问一个自己曾经认证过的网站并执行一些操作(如发邮件,发消息,甚至财产操作如转账和购买商品)。由于浏览器曾经认证过,所以被访问的网站会认为是真正的用户操作而去执行。这利用了 web 中用户身份验证的一个漏洞:简单的身份验证只能保证请求发自某个用户的浏览器,却不能保证请求本身是用户自愿发出的。
CSRF 与XSS 区别:
XSS: XSS漏洞——构造payload——发送给受害人——受害人点击打开——攻击者获取受害人的cookie——攻击者使用受害人cookie完成攻击
CSRF: CSRF漏洞——构造payload——发送给受害人——受害人点击打开——受害人执行代码——受害人完成攻击(不知情)
CSRF攻击流程:
从上图可以看出,要完成一次CSRF攻击,受害者必须依次完成两个步骤:
-
登录受信任网站A,并在本地生成Cookie。
-
在不登出A的情况下,访问危险网站B
看到这里,你也许会说:“如果我不满足以上两个条件中的一个,我就不会受到CSRF的攻击”。是的,确实如此,但你不能保证以下情况不会发生:
-
你不能保证你登录了一个网站后,不再打开一个tab页面并访问另外的网站。
-
你不能保证你关闭浏览器了后,你本地的Cookie立刻过期,你上次的会话已经结束。
所以 CSRF 是一种较难防御、又危险极大的漏洞。
三、漏洞原理
3.1 CSRF 漏洞实例
当我们打开或者登陆某个网站的时候,浏览器与网站所存放的服务器将会产生一个会话(cookies),在这个会话没有结束时,你就可以利用你的权限对网站进行操作。然而,攻击者就是利用这个特性,让受害者触发我们构造的表单或者语句,然后达到攻击者想要达到的目的。
这是我编写的一个简单的模拟网站后台管理的页面,用户名、密码都是admin
:
一般来说后台管理员都具有添加用户的功能,所以登录成功之后点击添加用户(这里为了方便演示,并没有让管理员自定义账户名、密码的功能,而是添加默认的账户和密码):
此处输入图片的描述页面没有回显,但后台已经成功添加用户,查看新添加的用户 :
此处输入图片的描述这时候如果我们记录下刚刚添加用户的网页地址,是否无论是哪个用户,只要访问这个地址就能添加用户呢?
为了验证这个想法,我们需要先注销登录(清楚cookie信息):
此处输入图片的描述
尝试在浏览器输入之前添加用户的页面地址:localhost/codeaudit/csrf/adduser.php
,尝试直接添加用户 :
但是并没有成功,页面也自动跳转到登录页面,为什么呢?因为adduser.php
页面需要验证session
信息才能执行相应操作。
但是有人想到:“既然我们自己不能成功访问这个页面,能否在管理员不知道的情况下,欺骗他访问这个页面呢?”,这就是CSRF 漏洞的产生原因。
具体的欺骗方式是:
我们在任意页面新建一个链接,此链接连接到的地址就是之前添加用户的地址:localhost/codeaudit/csrf/adduser.php
:
然后通过邮件或其他方式诱使管理员点击此链接(比如伪装成图片链接),然后就可以在管理员未发现的情况下添加用户:
此处输入图片的描述这就是一次利用CSRF漏洞添加后台的实例, 只要用户(受害者)点击该链接,就完成了一次CSRF攻击,虽然用户可能本身并没有执行该操作的意图。
在实际情况中,当管理员添加用户时,通常会自定义用户名和密码,而用户名和密码一般都是通过get方式直接在url中传输,所以我们伪造的连接中也可以自定义我们想用的用户名和密码。
四、漏洞防御
4.1 CSRF 漏洞防御策略
在服务器端防御CSRF攻击主要有四种策略:
- 验证HTTP Referer 字段
根据HTTP协议,在HTTP头中有一个字段叫Referer,它记录了该HTTP请求的来源地址。在通常情况下,访问一个安全受限页面的请求必须来自于同一个网站。比如某银行的转账是通过用户访问http://bank.test/test?page=10&userID=101&money=10000页面完成,用户必须先登录bank.test,然后通过点击页面上的按钮来触发转账事件。当用户提交请求时,该转账请求的Referer值就会是转账按钮所在页面的URL(本例中,通常是以bank. test域名开头的地址)。而如果攻击者要对银行网站实施CSRF攻击,他只能在自己的网站构造请求,当用户通过攻击者的网站发送请求到银行时,该请求的Referer是指向攻击者的网站。因此,要防御CSRF攻击,银行网站只需要对于每一个转账请求验证其Referer值,如果是以bank. test开头的域名,则说明该请求是来自银行网站自己的请求,是合法的。如果Referer是其他网站的话,就有可能是CSRF攻击,则拒绝该请求。
- 在请求地址中添加token并验证
CSRF攻击之所以能够成功,是因为攻击者可以伪造用户的请求,该请求中所有的用户验证信息都存在于Cookie中,因此攻击者可以在不知道这些验证信息的情况下直接利用用户自己的Cookie来通过安全验证。由此可知,抵御CSRF攻击的关键在于:在请求中放入攻击者所不能伪造的信息,并且该信息不存在于Cookie之中。鉴于此,系统开发者可以在HTTP请求中以参数的形式加入一个随机产生的token,并在服务器端建立一个拦截器来验证这个token,如果请求中没有token或者token内容不正确,则认为可能是CSRF攻击而拒绝该请求。
- 在HTTP头中自定义属性并验证
自定义属性的方法也是使用token并进行验证,和前一种方法不同的是,这里并不是把token以参数的形式置于HTTP请求之中,而是把它放到HTTP头中自定义的属性里。通过XMLHttpRequest这个类,可以一次性给所有该类请求加上csrftoken这个HTTP头属性,并把token值放入其中。这样解决了前一种方法在请求中加入token的不便,同时,通过这个类请求的地址不会被记录到浏览器的地址栏,也不用担心token会通过Referer泄露到其他网站。
- 添加验证码并验证
在表单中增加一个随机的数字或字母验证码,通过强制用户和应用进行交互,来有效地遏制CSRF攻击。
4.2 Token 防御 CSRF 实例
针对使用 token 防御 CSRF 攻击,我这里做一个简单的介绍。
-
要使得Token对于Hacker来说比较难以伪造,我们可以使用伪随机数生成器来构造它,然后把它放到服务器端的session里,并将Token发给用户(注意并没有保存在cookie中);
-
当用户提交请求时,服务器端拦截器将POST请求中的Token提取出来,与session中的Token进行比对,相等即执行下一步操作。
具体代码:
# 生成 token
session_start();
function set_token(){
$_SESSION['token'] = md5(time()+rand(1,5000));
}
# 使用 token 做验证
function check_token(){
if(isset($_POST['token'])&&$_POST['token'] === $_SESSION['token'])
{
return ture;
}
else
{
return false;
}
}
可以看到,同时验证了cookie中的sessionid与POST中的Token。