CSRF
全称Cross-Site Request Forgery
跨站请求伪造,也被称为One Click Attack
或Session Riding
,通常缩写为CSRF
或XSRF
。
攻击原因
CSRF
攻击是冒充信任用户向服务器发送非预期请求的一种攻击方式
例如:CSRF
攻击者在用户已经登录目标站点后,诱使用户访问一个攻击页面,利用目标站点对用户的信任,以用户身份对目标站点发起伪造用户操作的请求,以达到攻击目的。
CSRF
攻击源于Web
的隐式身份验证机制:Web
的身份验证机制虽然可以保证一个请求是来自于某个用户的浏览器,但却无法保证该请求是用户批准发送的,CSRF
攻击一般由服务器解决。
检测CSRF
漏洞最简单的方式就是抓取一个正常的请求数据包,去掉Refer
字段后再重新提交,如果该提交还有效,那么基本上可以确定存在CSRF
漏洞。
防御手段
- 尽量使用
POST
限制使用GET
GET
接口太容易被拿来做CSRF
攻击,使用POST
方式以降低攻击风险。 - 添加验证码
验证码强制用户必须与应用进行交互,才能完成最终请求。通常情况下,验证码能够很好的遏制CSRF攻击。但处于用户体验考虑,验证码做出辅助手段会比较不错。 Anti CSRF Token
RESTful API
Referer Check
浏览器Cookie策略
浏览器的Cookie
可以分为:
-
Session Cookie
会话Cookie
,保存在内存中,浏览器关闭后立即失效。 -
Third-party Cookie
第三方Cookie
,保存在本地,只有到了失效时间才会失效的Cookie
。
若HTTP请求返回的响应头中包含了P3P Header
字段,则允许浏览器发送第三方Cookie
。
浏览器默认的Cookie
策略:
-
IE6
、IE7
、IE8
、Safari
默认会拦截第三方本地Cookie
的发送 -
FireFox2
、FireFox3
、Opera
、Chrome
、Android
等默认不会拦截
通过浏览器Cookie
策略来防御CSRF
攻击并不靠谱,只能说是降低了风险。
Referer Check
Referer Check
也就是验证HTTP
请求中的Referer
字段
Referer Check
在Web中最常见的应用是“防盗链”,例如防止图片盗链。同理,也可以用来检查请求是否来自合法的“源”Referer
,即判断是否是来自指定页面或域。如果不是则极有可能是CSRF
攻击。但因服务器并不是什么时候都能获取到Referer
,所以无法作为CSRF
防御的主要手段。通过Referer Check
来监控CSRF
攻击的发生,倒是一种可行的方案。
根据HTTP
协议,在HTTP
头中有一个Referer
字段记录了该HTTP
请求的来源地址,正常情况下访问一个安全受限的页面请求都是来自于同一个站点的。因此要防御CSRF
攻击,只需要对每个请求验证其Referer
值。虽然这个方法简单易行,但并非万无一失。
由于Referer
值是由浏览器提供的,虽然HTTP
协议上有明确要求,但每个浏览器对Referer
的具体实现存在差别,并不能保证浏览器自身没有安全漏洞。
使用验证Referer
值得方式,就是把安全性都依赖于第三方浏览器来保证。从理论上来讲,这样并不安全。
用户可以设置浏览器使其在发送请求时不再提供Referer
,当他们正常访问站点时,站点会因为请求没有Referer
值而认为是CSRF
攻击,拒绝合法用户的访问。
Anti CSRF Token
现在对CSRF
攻击的防御一致的做法是使用一个Token
比如:当用户访问某个表单时,服务器会提前生成一个随机的Token
,存放到用户会话Session
中或浏览器Cookie
中,并在表单中附带这个Token
参数。当用户提交请求后,服务器验证表单中的Token
是否与用户Session
或Cookie
中的Token
一致,若一致则为合法请求否则为非法请求。需要注意的是Token
的保密性和随机性。
CSRF
的Token
仅仅用于对抗CSRF
攻击,如果同时又存在XSS
漏洞时,这个方案也时空谈。
在请求地址中添加token并验证(Secure Token)
CSRF攻击之所以能够成功,是因为黑客可以完全伪造用户的请求,该请求中所有的用户验证信息都存在于Cookie中,因此黑客可能在不知道这些验证信息的情况下直接利用自己的Cookie来通过安全验证。要抵御CSRF关键自于在请求中放入黑客所不能伪造的信息,并且该信息不存在Cookie之中。
可以在HTTP请求中以参数的形式加入一个随机产生的Token,并在服务器端建立一个拦截器来验证这个Token,如果请求中没有Token或Token内容不正确,则认为可能是CSRF攻击而拒绝该请求。
这种方法比要检查Refer安全一些,Token可以在用户登录后产生并放在Session之中,然后每次请求时把Token从Session中拿出于请求中的Token进行比对。对于GET请求,可以直接将Token以参数形式附加到请求地址之后。对于POST请求,可以在表单中添加一个隐藏域。注意,需要为Token设置失效时间。
但这个方法的难点在于:如何把Token以参数的形式加入请求。为什么了,因为一个站带中可以接受请求的地方非常多,对于每个请求都添加Token是很麻烦的,并且容易漏掉。
通常使用的方法是每次页面加载时,使用JavaScript遍历整个DOM树,对于DOM中所有的锚点和
标签后加入Token,这样可以解决大部分的请求,但是对于在页面加载之后动态生成生成HTML代码时这个方法就没有作用,还是需要开发人员编码时手工添加Token。
这种方法有一个缺点是难以保证Token本身的安全,当黑客在站点中发表自己的站点链接地址时,由于系统会为地址添加Token,黑客可在自己的站点上得到这个Token,并马上发送CSRF攻击。为了避免这一点,系统可以在添加Token的时候增加一个判断,如果这个链接是站内的就添加Token,如果是外链则不添加。不过即使Token不以参数的形式附加到请求之中,黑客的站点也同样可以通过Refer来得到这个Token值发送CSRF攻击,这也是一些用户喜欢手动关闭浏览器Refer的原因。
在HTTP头中自定义属性并验证
这种方式也是使用Token进行验证,和“在请求地址中添加token并验证”的方法不同的是,这里并不是将Token以参数的形式放到HTTP请求之中,而是把Token放到HTTP头的自定义属性中。通过XMLHttpRequest类可以一次性给所有该类请求添加Token。同时,通过XMLHttpRequeset请求的地址不会被记录到浏览器的地址栏,也不用担心Token会透过Refer泄漏到其它网站中区。
然后这种方法的局限性很大,XMLHttpRequest请求只能用于AJAX方法中,并非所有请求都适合使用AJAX发起请求,而且通过XMLHttpRequest请求得到的页面不能被浏览器记录,从而进行前进、后退、刷新、收藏等操作时,给用户带来不便。
另外,对于没有进行CSRF防护的系统采用这种方法来进行防护,要把所有请求都改造成XMLHttpRequest请求,这样几乎重写整个系统,代价无疑是不能接受的。
未完待续...