简介
CSRF(Cross-site request forgery),中文名称:跨站请求伪造。尽管听起来像跨站脚本(XSS),但它与XSS非常不同,并且攻击方式几乎相左。XSS利用站点内的信任用户,而CSRF则通过伪装来自受信任用户的请求来利用受信任的网站。与XSS攻击相比,CSRF攻击往往不大流行和难以防范,所以被认为比XSS更具危险性。
CSRF攻击可以以你的名义发送恶意请求。CSRF能够做的事情包括:以你名义发送邮件,发消息,盗取你的账号,甚至于购买商品,虚拟货币转账......造成的问题包括:个人隐私泄露以及财产安全。

CSRF攻击原理

图中服务器A是受信任/被攻击的网站,服务器B是恶意网站/攻击网站。
1)用户打开浏览器,访问受信任网站A,输入用户名和密码登陆请求登陆网站A。
2)网站A验证用户信息,用户信息通过验证后,网站A产生Cookie信息并返回给浏览器。
3)用户未退出网站A之前,在同一浏览器中,打开一个TAB访问网站B。
4)网站B接受到用户请求后,返回一些攻击性代码。
5)浏览器在接受到这些攻击性代码后,促使用户不知情的情况下浏览器携带Cookie(包括sessionId)信息,请求网站A。这种请求有可能进行删除、更新等操作。
如银行转账链接为:
http://bank.example/withdraw?account=bob& amount=1000000&for=Mallory。
这时,Mallory 想到使用 CSRF 的攻击方式,他先自己做一个网站,在网站中放入如下代码:

并且通过广告等诱使 Bob 来访问他的网站。当 Bob 访问该网站时,上述 url 就会从 Bob 的浏览器发向银行,而这个请求会附带 Bob 浏览器中的 cookie 一起发向银行服务器。大多数情况下,该请求会失败,因为他要求 Bob 的认证信息。但是,如果 Bob 当时恰巧刚访问他的银行后不久,他的浏览器与银行网站之间的 session 尚未过期,浏览器的 cookie 之中含有 Bob 的认证信息。这时,钱将从 Bob 的账号转移到 Mallory 的账号。

从上面CSRF攻击原理可以看出,要完成一次CSRF攻击,需要被攻击者完成两个步骤:
1.登陆受信任网站A,并在本地生成COOKIE。
2.在不登出A的情况下,访问危险网站 B。
看到这里,你也许会说:“如果我不满足以上两个条件中的一个,我就不会受到CSRF的攻击”。是的,确实如此,但你不能保证以下情况不会发生:
1.你不能保证你登录了一个网站后,不再打开一个tab页面并访问另外的网站。
2.你不能保证你关闭浏览器了后,你本地的Cookie立刻过期,你上次的会话已经结束。(事实上,关闭浏览器不能结束一个会话,但大多数人都会错误的认为关闭浏览器就等于退出登录/结束会话了......)
Cookie分两种:一种是“Session Cookie”,又称“临时Cookie”;另一种是“Third-party Cookie”,也成为“本地Cookie”。
3.上图中所谓的攻击网站,可能是一个存在其他漏洞的可信任的经常被人访问的网站。如该信任网站被进行XSS攻击。
总结:在讨论如何抵御 CSRF 之前,先要明确 CSRF 攻击的对象,也就是要保护的对象。从以上的例子可知,CSRF 攻击是黑客借助受害者的 cookie 骗取服务器的信任,但是黑客并不能拿到 cookie,也看不到 cookie 的内容。另外,对于服务器返回的结果,由于浏览器同源策略的限制,黑客也无法进行解析。因此,黑客无法从返回的结果中得到任何东西,他所能做的就是给服务器发送请求,以执行请求中所描述的命令,在服务器端直接改变数据的值,而非窃取服务器中的数据。所以,我们要保护的对象是那些可以直接产生数据改变的服务,而对于读取数据的服务,则不需要进行 CSRF 的保护。比如银行系统中转账的请求会直接改变账户的金额,会遭到 CSRF 攻击,需要保护。而查询余额是对金额的读取操作,不会改变数据,CSRF 攻击无法解析服务器返回的结果,无需保护。

CSRF防御
一般来说,防御CSRF的方法有三种:验证HTTP Referer、验证码和token。
验证HTTP Referer
根据 HTTP 协议,在 HTTP 头中有一个字段叫 Referer,它记录了该 HTTP 请求的来源地址。在通常情况下,访问一个安全受限页面的请求来自于同一个网站,比如需要访问 http://www.aaa.com?a=1&b=2,用户必须先登陆 www.aaa.com,然后通过点击页面上的按钮来触发转账事件。这时,该转帐请求的 Referer 值就会是转账按钮所在的页面的 URL,通常是以 aaa 域名开头的地址。而如果黑客要对银行网站实施 CSRF 攻击,他只能在他自己的网站构造请求,当用户通过黑客的网站发送请求到银行时,该请求的 Referer 是指向黑客自己的网站。因此,要防御 CSRF 攻击,网站只需要对于每一个转账请求验证其 Referer 值,如果是以 aaa 开头的域名,则说明该请求是来自银行网站自己的请求,是合法的。如果 Referer 是其他网站的话,则有可能是黑客的 CSRF 攻击,拒绝该请求。
但是,Referer 的值是由浏览器提供的,虽然 HTTP 协议上有明确的要求,但是每个浏览器对于 Referer 的具体实现可能有差别,并不能保证浏览器自身没有安全漏洞。使用验证 Referer 值的方法,就是把安全性都依赖于第三方(即浏览器)来保障,从理论上来讲,这样并不安全。
对于低版本的浏览器来说,黑客可以篡改Referer值,即使是用户使用了高版本的浏览器,黑客无法篡改其值,但有些用户或组织担心Referer值会把一些信息泄露,多以可以设置浏览器在发送请求的时候不发送Referer值,导致服务器接收不到Referer值而认为是CSRF攻击,拒绝合法用户的访问。

验证码
用户在进行一个操作请求时,令用户输入一个验证码,可以防止CSRF的攻击,因为CSRF攻击只能模拟用户发送请求,但是获取不到请求结果。因此攻击者获取不到页面上的验证码信息,从而无法进行CSRF攻击。
但是用户在进行每个业务请求时,都输入验证码,大大降低了用户体验。所以一般做法是在一些重要操作进行验证码的校验,如支付页面。但是当页面如果存在XSS漏洞时,攻击者可以获取到图片验证信息,因此一般做法是使用手机验证码验证或者使用输入支付密码进行校验。这样才能确保安全。

token校验
CSRF 攻击之所以能够成功,是因为黑客可以完全伪造用户的请求,该请求中所有的用户验证信息都是存在于 cookie 中,因此黑客可以在不知道这些验证信息的情况下直接利用用户自己的 cookie 来通过安全验证。要抵御 CSRF,关键在于在请求中放入黑客所不能伪造的信息,并且该信息不存在于 cookie 之中。可以在 HTTP 请求中以参数的形式加入一个随机产生的 token,并在服务器端建立一个拦截器来验证这个 token,如果请求中没有 token 或者 token 内容不正确,则认为可能是 CSRF 攻击而拒绝该请求。
这种方法要比检查 Referer 要安全一些,token 可以在用户登陆后产生并放于 session 之中,然后在每次请求时把 token 从 session 中拿出,与请求中的 token 进行比对,但这种方法的难点在于如何把 token 以参数的形式加入请求。对于 GET 请求,token 将附在请求地址之后,这样 URL 就变成 http://url?csrftoken=tokenvalue。 而对于 POST 请求来说,要在 form 的最后加上 ,这样就把 token 以参数的形式加入请求了。但是,在一个网站中,可以接受请求的地方非常多,要对于每一个请求都加上 token 是很麻烦的,并且很容易漏掉,通常使用的方法就是在每次页面加载时,使用 javascript 遍历整个 dom 树,对于 dom 中所有的 a 和 form 标签后加入 token。这样可以解决大部分的请求,但是对于在页面加载之后动态生成的 html 代码,这种方法就没有作用,还需要程序员在编码时手动添加 token。
该方法还有一个缺点是难以保证 token 本身的安全。特别是在一些论坛之类支持用户自己发表内容的网站,黑客可以在上面发布自己个人网站的地址。由于系统也会在这个地址后面加上 token,黑客可以在自己的网站上得到这个token,并马上就可以发动 CSRF 攻击。为了避免这一点,系统可以在添加 token的时候增加一个判断,如果这个链接是链到自己本站的,就在后面添加 token,如果是通向外网则不加。

遍历dom树添加token的方法

var token="asdfasdf";
function appendToken(){
updateForms();
updateTags();
}

function updateForms() {
// 得到页面中所有的 form 元素
var forms = document.getElementsByTagName('form');
for(i=0; i

发表评论

您的电子邮箱地址不会被公开。 必填项已用*标注

Captcha Code