CSRF防御方案

一般网站有三种防御CSRF攻击的方案。(1)验证token值。(2)验证HTTP头的Referer。(3)用XMLHttpRequest附加在header里。以上三种方法都在广泛使用,但是他们的效果都不是那么的令人满意。(结尾有惊喜!)

一、 Token验证

在每个HTTP请求里附加一部分信息是一个防御CSRF攻击的很好的方法,因为这样可以判断请求是否已经授权。这个“验证token”应该不能轻易的被未登录的用户猜测出来。如果请求里面没有这个验证token或者token不能匹配的话,服务器应该拒绝这个请求。

Token验证的方法可以用来防御登陆CSRF,但是开发者往往会忘记验证,因为如果没有登陆,就不能通过session来绑定CSRF token。网站要想用验证token的方式来防御登陆CSRF攻击的话,就必须先创建一个“前session”,这样才能部署CSRF的防御方案,在验证通过了之后,再创建一个真正的session。

Token的设计。有很多技术可以生成验证token。

• session标识符。浏览器的cookie存储方式就是为了防止不同域之间互相访问cookie。一个普遍的做法是直接利用用户的session标识符来作为验证token。服务器在处理每一个请求时,都将用户的token与session标识符来匹配。如果攻击者能够猜测出用户的token,那么他就能登录用户的账户。而且这样做有个不好的地方在于,偶尔用户正在浏览的内容会发送给第三方,比如通过电子邮件直接上网页内容上传到浏览器厂商的bug跟踪数据库。如果正好这个页面包含有用户的session标识符,任何能看到这个页面的人都能模拟用户登陆到网站,直到会话过期。

• 独立session随机数。与直接使用用户的session标识符不一样的是,当用户第一次登陆网站的时候,服务器可以产生一个随机数并将它存储在用户的cookie里面。对于每一个请求,服务器都会将token与存储在cookie里的值匹配。例如,广泛使用的Trac问题跟踪系统就是用的此技术。但是这个方法不能防御主动的网络攻击,即使是整个web应用都使用的是HTTPS协议。因为攻击者可以使用他自己的CSRF token来覆盖来覆盖这个独立session随机数,进而可以使用一个匹配的token来伪造一个跨站请求。

• 依赖session随机数。有一个改进产生随机数的方法是将用户的session标识符与CSRF token建立对应关系后存储在服务端。服务器在处理请求的时候,验证请求中的token是否与session标识符匹配。这个方法有个不好的地方就是服务端必须要维护一个很大的对应关系表(哈希表)。

• session标识符的HMAC。有一种方法不需要服务端来维护哈希表,就是可以对用户的session token做一个加密后用作CSRF 的token。例如, Ruby on Rails的web程序一般都是使用的这种方法,而且他们是使用session标识符的HMAC来作为CSRF token的。只要所有的网站服务器都共享了HMAC密钥,那么每个服务器都可以验证请求里的CSRF token 是否与session标识符匹配。HMAC的特性能确保即使攻击者知道用户的CSRF token,也不能推断出用户的session标识符。

鉴于有充足的资源,网站都可以使用HMAC方法来防御CSRF攻击。但是,很多网站和一些CSRF的防御框架(比如NoForge, CSRFx 和CSRFGuard)都不能正确的实现比较隐秘的token防御。一个常见的错误就是在处理跨站请求的时候暴露了CSRF token。举个例子,一个可信的网站在对另一个网站发起请求的时候附加上了CSRF token,那么那个网站就可以对这个可信的网站伪造一个跨站请求。

案例研究:NoForge.NoForge就是使用服务端保存哈希表的方式来验证用户的CSRF token。它在所有链接和表单提交的时候会附加一个CSRF token,造成这种技术不太完善的原因有以下三个:

1、HTML是在浏览器里动态创建的,而不会被重新加上CSRF token。有些网站是在客户端创建HTML的。比如Gmail, Flickr, 和 Digg都是用JavaScript 来创建表单,而这些表单正是需要CSRF防御措施的。

2、NoForge并没有对指向本站和外站的超链接作区分。如果有一个指向外站的超链接,那么外站可以用请求里面获取到用户的CSRF token。比如,如果phpBB部署了NoForge,那么一旦用户点击了一个连接,连接的站点就可以获取到用户的CSRF token,即使NoForge区分了是本站的链接还是外站的链接,因为Referer 还是会暴露用户的CSRF token。

3、NoForge对登陆CSRF并没有什么效果,因为如果用户已经有了session标识符(登陆了),那么NoForge只会验证CSRF token。尽管这种缺陷是可以修复,但是这也说明了要想正确的实施token验证策略并不是一件很容易的事情。

虽然上述三个原因都是可以修复的,但是这些缺陷都说明了要想正确地实施token验证策略,是很复杂的一件事情。CSRFx 和 CSRFGuard,还有很多网站都说明了这一问题。

二、 Referer

大多数情况下,当浏览器发起一个HTTP请求,其中的Referer标识了请求是从哪里发起的。如果HTTP头里包含有Referer的时候,我们可以区分请求是同域下还是跨站发起的,因为Referer离标明了发起请求的URL。网站也可以通过判断有问题的请求是否是同域下发起的来防御CSRF攻击。

不幸的是,通常Referer会包含有一些敏感信息,可能会侵犯用户的隐私。比如,Referer可以显示用户对某个私密网站的搜索和查询。尽管这 些内容对私密网站站长来说是好事,因为他们可以通过这些内容来优化搜索引擎排名,但是一些用户还是认为侵犯了他们的隐私。另外,许多组织也很担忧 Referer可能会将内网的一些机密信息泄露出去。

漏洞。从历史上来看,浏览器的一些漏洞使得一些恶意网站有欺骗Referer的价值,尤其是在使用代理服务器的时候。很多对Referer欺骗的讨论都标明浏览器允许Referer可以伪造。Mozilla在Fire-fox 1.0.7里面已经修复了Referer欺骗的漏洞。目前的IE则还有这方面的漏洞,但是这些漏洞只能影响XMLHttpRequest,并且只能用来伪造Referer跳转到攻击者自己的网站。

尺度。如果网站选择使用Referer来防御CSRF攻击的话,那么网站的开发人员就需要决定到底是使用比较宽松还是比较严格的Referer验证策略。如果采用宽松的Referer验证策略,网站就应该阻止Referer值不对的请求。如果请求里面没有Referer,就接收请求。尽管这个方法用的很普遍,但是它很容易被绕过。因为攻击者可以在header里面去掉Referer。例如,FTP和数据URL发起的请求里面就不包含Referer。 如果使用严格的Referer验证策略,网站还要阻止没有Referer的请求。这样做主要是为了防止恶意网站主动隐藏Referer,但也会带来兼容性 问题,比如会误杀一部分合法的请求,因为有些浏览器和网络的设置默认就是不含有Referer的。所以说这个度一定要掌握好,很多时候取决于经验。我们还 会在4.2.1里讨论这个问题。

个案研究:Facebook。纵观Facebook的大部分网站都是使用token认证的方式来防御CSRF攻击的。但是,在Facebook的登陆框部分则使用的是宽松的Referer验证策略。这种方法在面对登陆CSRF的攻击时没有什么作用。举例来说,攻击者可以讲用户从http://attacker.com/重定向到ftp://attacker.com/index.html ,然后再对Facebook发起一个跨站的登陆请求。因为请求来自FTP URL,所以大多数浏览器都不会在请求里包含Referer。

4.2.1 实验

为了评估严格的Referer验证策略的兼容性,我们进行了一项实验来衡量到底有多大概率以及在什么情况下,合法的请求里面不含有Referer。

设计。广告是一个很方便测量浏览器和网络特征的渠道,因此我们可以利用广告作为实验平台。在2008年4月5日到4月8日期间,我们从163,767个独立IP购买了283,945 个广告,分别是两个不同的广告渠道。在渠道A,我们以每千次展示0.50美元的价格购买了网络旗帜广告,关键字为“火狐”,“游戏”,“IE”,“视频”,“YouTube”。在渠道B,我们以每千次展示5美元的价格的间隙广告,关键字为“芭蕾”,“金融“,“花”,“食品”和“园艺”。我们在每个广告渠道上花了100美元,渠道A有241,483点击量(146,310个独立IP),渠道B有42,406点击量(18,314个独立IP)。

广告服务是由我们实验室里的两台主机提供,两个独立的域名是从不同的注册商处购买。每当显示广告时,广告会在接下来的每个请求里面生成一个特定的标识符,并随机选择一台主机作为主服务器。主服务器通过HTTP或者HTTPS协议将客户端HTML发送到我们的服务器,这些HTML能发起一个GET或者POST请求。其中,请求包括提交表单,图像请求和XMLHttpRequests。请求的顺序是随机的并且跟用户的操作无关。当广告通过了浏览器的安全策略之后,就向主服务器发起一个同域的请求,同时向次服务器发起一个跨域请求。每个服务器的成本是400美元,域名是7美元,从一个合法的证书颁发机构获得的90天域验证的HTTPS证书是免费的。服务器根据接收到的网络请求来记录请求参数,包括Referer,User-Agent头,日期,客户端的C类网络,会话标识符。服务器还通过DOM API记录了document.referrer的值,但是不记录客户端的IP地址。为了统计独立的IP地址,服务器利用一个随机产生的KEY而不是记录HMAC的方式,这个KEY会被丢弃。服务器记录的信息不足以单独确定广告的浏览者到底有多少。

伦理。实验的设计遵守两个广告渠道的规则。实验中的行为基本上都是web广告每天的行为,所以都能正常的从广告商那里请求额外的资源,比如图片,音频和视频。尽管我们的广告产生的HTTP请求数目远大于普通的广告,但是我们需要的带宽明显比一个视频广告需要的带宽要小。我们的服务器也像广告商一样,只记录他们所记录的信息。实际上我们的服务器记录的信息明显要比商业的广告商要少,因为我们并不记录客户端的IP地址。

结果。我们已经将结果在图2和图3里总结出来了,我们还发现以下结果只有95%的可信度。

• HTTP方法里, 跨域请求比同域请求不包含Referer头的情况更普遍,而在POST方法(卡方系数= 2130, p值<0.001) 和GET方法(卡方系数= 2175, p值<0.001) 里比较,前者不包含Referer头的情况更为普遍。

• 在不包含Referer头的统计中,HTTP比HTTPS更为普遍,包括跨域POST(卡方系数= 6754, p值<0.001)请求,跨域GET(卡方系数= 6940, p值<0.001)请求,同域POST(卡方系数= 2286, p值<0.001)请求和同域GET请求(卡方系数= 2377, p值<0.001)。

• 在不包含Referer头的统计中,广告渠道B所有形式的请求都比A要更普遍。这些请求形式包括:HTTP跨域POST(卡方系数= 3060, p值<0.001),HTTP同域POST(卡方系数= 6537, p值<0.001),HTTPS跨域POST(卡方系数= 49.13, p值<0.001)和HTTPS同域POST(卡方系数= 44.52, p值<0.001)请求。

• 我们还统计了自定义的header X-Requested-By(参见4.3节)和Origin(见第5章),X-Requested-By大概有0.029%到0.047%的HTTP POST请求,0.084%到0.112%的HTTP GET请求,0.008%到0.018%的HTTPS POST请求和 0.009%到0.020%的HTTPS GET请求里不包含有Referer头。Origin则在与上述相同的请求里都不包含Referer头。

图2. 不包含Referer和Referer不正确的请求(283,945 个结果)。x和y分别代表主服务器和次服务器的域名

讨论。下面有两个有力的证据可以表明在不包含Referer的请求里,通常是来自网络(攻击)而不是浏览器。

1、HTTP请求比HTTPS请求不包含Referer更为普遍是因为,网络代理可以删除HTTP请求里的header,但是不能删除HTTPS请求里的header。当然,在一些企业的网络里,一些HTTPS的终端就是一个网络代理,这种情况下代理可以修改HTTPS请求,但是这种情况是比较罕见的。

2、浏览器在去掉Referer的时候也会去掉document.referrer的值,但是如果Referer是在网络里去掉的话,document.referrer却还在。但是我们发现,Referer去掉的情况比document.referrer去掉的情况要更为普遍。

实际上,在实验中,document.referrer值被去掉主要是因为两种特殊的浏览器:PlayStation 3 浏览器不支持document.referrer,Opera去掉document.referrer(但是并不去掉Referer)是为了跨站HTTPS请求。XMLHttpRequest中的Referer被去掉的比例较高是由于Firefox 1.0和1.5中的bug引起的。所有的这些结果都表明只有极少数的浏览器被配置成不发送Referer。

也有证据表明,Referer被去掉是由于涉及到隐私问题,当浏览器把Referer从网站A发送到网站B时,用户的隐私也在被暴露,因为网站B可以通过Referer来收集用户在网站A的浏览行为。相比之下,在同域下发送Referer则不会引起隐私问题,因为网站完全可以通过cookie来收集用户的隐私(也就是完全没有必要通过Referer来收集)。我们还发现,跨站请求比同站请求要更多的阻止Referer,说明由于考虑到隐私的问题,所以才会人为的阻止Referer发送。

由此,我们得出两个主要的结论:

1、通过HTTPS来防御CSRF。在HTTPS请求里,Referer可以被用来防御CSRF。为了实施用Referer来防御CSRF的策略,网站必须拒绝那些没有Referer的请求,因为攻击者可以控制浏览器来去掉Referer。而在HTTP里,网站则不能一味的拒绝没有Referer的请求,因为考虑到兼容性,可能有相当大一部分 (大约 3–11%)用户可能就访问不了网站了。不同的是在HTTPS里,则可以执行严格的Referer验证策略,因为只有很小的一部分(0.05–0.22%)浏览器会去掉Referer。特别需要指出的是,严格的Referer验证策略非常适合用来防御登陆CSRF,因为通常情况下,登陆请求都是通过HTTPS协议发起的。

2、隐私问题。严格的Referer策略是很好的CSRF的防御方案,因为它实施起来很简单。不幸的是,隐私策略可能会阻止此方案的流行。因此,浏览器新的安全性能和新的CSRF防御机制都必须要先解决好隐私问题,才能大规模的部署。

图3. 广告渠道A中不包含Referer和Referer不正确的请求(241,483 个结果)。Opera阻止了跨站的HTTPS document.referrer,Firefox 1.0和1.5由于bug在XMLHttpRequest的时候不发送Referer,PlayStation 3(图中即为PS)不支持document.referrer。

三、自定义HTTP header

我们也可以用自定义HTTP头的方法来防御CSRF攻击,因为虽然浏览器会阻止向外站发送自定义的HTTP头,但是允许向本站通过XMLHttpRequest的方式发送自定义HTTP头。比如,prototype.js这个JavaScript库就是使用这种方法,并且增加了 X-Requested-By头到XMLHttpRequest里面 。Google Web Toolkit 也建议开发者用在XMLHttpRequest里增加一个X-XSRF-Cookie头的方法来防御CSRF攻击,其中XMLHttpRequets包含有cookie的值。当然XMLHttpRequets里面的cookie并不需要用来防御CSRF,因为只需要有头部分就足够了。

在使用这种方法来防御CSRF攻击的时候,网站必须在所有的请求里使用XMLHttpRequest并附加一个自定义头(比如X-Requested-By),并且拒绝所有没有自定义头的的请求。例如,为了防御登陆CSRF的攻击,网站必须通过XMLHttpRequest的方式发送用户的身份验证信息到服务器。在我们的实验里,在服务器接收到的请求里面,大约有99.90–99.99%的请求是含有X-Requested-By头的,这表明这一方法适用于绝大多数的用户。

 学习路线

对于从来没有接触过网络安全的同学,我们帮你准备了详细的学习成长路线图。可以说是最科学最系统的学习路线,大家跟着这个大的方向学习准没问题。(需要的可以在评论区留言哦)

同时每个成长路线对应的板块都有配套的视频提供:

猜你喜欢

转载自blog.csdn.net/hdwlwang/article/details/130320192