购买
下载掌阅APP,畅读海量书库
立即打开
畅读海量书库
扫码下载掌阅APP

4.3 CSRF的防御

CSRF攻击是一种比较奇特的攻击,下面看看有什么方法可以防御这种攻击。

4.3.1 验证码

验证码被认为是对抗CSRF攻击最简洁而有效的防御方法。

CSRF攻击的过程,往往是在用户不知情的情况下构造了网络请求。而验证码,则强制用户必须与应用进行交互,才能完成最终请求。因此在通常情况下,验证码能够很好地遏制CSRF攻击。

但是验证码并非万能。很多时候,出于用户体验考虑,网站不能给所有的操作都加上验证码。因此,验证码只能作为防御CSRF的一种辅助手段,而不能作为最主要的解决方案。

4.3.2 Referer Check

Referer Check在互联网中最常见的应用就是“防止图片盗链”。同理,Referer Check也可以被用于检查请求是否来自合法的“源”。

常见的互联网应用,页面与页面之间都具有一定的逻辑关系,这就使得每个正常请求的Referer具有一定的规律。

比如一个“论坛发帖”的操作,在正常情况下需要先登录到用户后台,或者访问有发帖功能的页面。在提交“发帖”的表单时,Referer的值必然是发帖表单所在的页面。如果Referer的值不是这个页面,甚至不是发帖网站的域,则极有可能是CSRF攻击。

即使我们能够通过检查Referer是否合法来判断用户是否被CSRF攻击,也仅仅是满足了防御的充分条件。 Referer Check的缺陷在于,服务器并非什么时候都能取到Referer。 很多用户出于隐私保护的考虑,限制了Referer的发送。在某些情况下,浏览器也不会发送Referer,比如从HTTPS跳转到HTTP,出于安全的考虑,浏览器也不会发送Referer。

在Flash的一些版本中,曾经可以发送自定义的Referer头。虽然Flash在新版本中已经加强了安全限制,不再允许发送自定义的Referer头,但是难免不会有别的客户端插件允许这种操作。

出于以上种种原因,我们还是无法依赖于Referer Check作为防御CSRF的主要手段。但是通过Referer Check来监控CSRF攻击的发生,倒是一种可行的方法。

4.3.3 Anti CSRF Token

现在业界针对CSRF的防御,一致的做法是使用一个Token。在介绍此方法前,先了解一下CSRF的本质。

4.3.3.1 CSRF的本质

CSRF为什么能够攻击成功?其本质原因是 重要操作的所有参数都是可以被攻击者猜测到的。

攻击者只有预测出URL的所有参数与参数值,才能成功地构造一个伪造的请求;反之,攻击者将无法攻击成功。

出于这个原因,可以想到一个解决方案:把参数加密,或者使用一些随机数,从而让攻击者无法猜测到参数值。这是“不可预测性原则”的一种应用(参考“我的安全世界观”一章)。

比如,一个删除操作的URL是:

把其中的username参数改成哈希值:

这样,在攻击者不知道salt的情况下,是无法构造出这个URL的,因此也就无从发起CSRF攻击了。而对于服务器来说,则可以从Session或Cookie中取得“username=abc”的值,再结合salt对整个请求进行验证,正常请求会被认为是合法的。

但是这个方法也存在一些问题。首先,加密或混淆后的URL将变得非常难读,对用户非常不友好。其次,如果加密的参数每次都改变,则某些URL将无法再被用户收藏。最后,普通的参数如果也被加密或哈希,将会给数据分析工作带来很大的困扰,因为数据分析工作常常需要用到参数的明文。

因此,我们需要一个更加通用的解决方案来帮助解决这个问题。这个方案就是使用Anti CSRF Token。

回到上面的URL中,保持原参数不变,新增一个参数Token。这个Token的值是随机的,不可预测:

Token需要足够随机,必须使用足够安全的随机数生成算法,或者采用真随机数生成器(物理随机,请参考“加密算法与随机数”一章)。Token应该作为一个“秘密”,为用户与服务器所共同持有,不能被第三者知晓。在实际应用时,Token可以放在用户的Session中,或者浏览器的Cookie中。

由于Token的存在,攻击者无法再构造出一个完整的URL实施CSRF攻击。

Token需要同时放在表单和Session中。在提交请求时,服务器只需验证表单中的Token,与用户Session(或Cookie)中的Token是否一致,如果一致,则认为是合法请求;如果不一致,或者有一个为空,则认为请求不合法,可能发生了CSRF攻击。

如下这个表单中,Token作为一个隐藏的input字段,放在form中:

隐藏字段中的Token

同时Cookie 中也包含了一个Token:

Cookie中的Token

4.3.3.2 Token的使用原则

Anti CSRF Token在使用时,有若干注意事项。

防御CSRF的Token,是根据“不可预测性原则”设计的方案,所以Token的生成一定要足够随机,需要使用安全的随机数生成器生成Token。

此外,这个Token的目的不是为了防止重复提交。所以为了使用方便,可以允许在一个用户的有效生命周期内,在Token消耗掉前都使用同一个Token。但是如果用户已经提交了表单,则这个Token已经消耗掉,应该再次重新生成一个新的Token。

如果Token保存在Cookie中,而不是服务器端的Session中,则会带来一个新的问题。如果一个用户打开几个相同的页面同时操作,当某个页面消耗掉Token后,其他页面的表单内保存的还是被消耗掉的那个Token,因此其他页面的表单再次提交时,会出现Token错误。在这种情况下,可以考虑生成多个有效的Token,以解决多页面共存的场景。

最后,使用Token时应该注意Token的保密性。Token如果出现在某个页面的URL中,则可能会通过Referer的方式泄露。比如以下页面:

这个manage页面是一个用户面板,用户需要在这个页面提交表单或者单击“删除”按钮,才能完成删除操作。

在这种场景下,如果这个页面包含了一张攻击者能指定地址的图片:

则“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防御就是空中楼阁。安全防御的体系是相辅相成、缺一不可的。 ucFMaeiOBXpNn4MfhD/mKg9bYgEbDkj0UBUtndL2fiPhATHxasZOdYwUIchZZDOI

点击中间区域
呼出菜单
上一章
目录
下一章
×