概述
CSRF是Cross Site Request Forgery的缩写,中文是跨站点请求伪造;接下来将和大家分享这种攻击的原理、实施的方法、以及防御的几种方案;
CSRF攻击的原理
通过在恶意网站部署好攻击代码和相关数据,然后引导目标网站的已经授权的用户进入恶意网站,由于浏览器已经获得了目标网站的用户授权票据,因此恶意网站就可以执行“事先”部署好的代码向目标网站提交数据使目标网站执行一些写的操作,比如删除目标网站的数据、向目标网站提交垃圾数据等,然而这个过程是在后台默默执行的,用户毫不知情。
举个例子说明一下吧:
假设目标网站www.a.com,有一个页面www.a.com/blog/delete.aspx?id=123 是删除ID为123的博文操作;
那么攻击者就可以在恶意网站www.b.com/csrfpage.aspx页面部署下面的代码:
<form id="csrffrm" action="http://www.a.com/blog/delete.aspx" target="hideiframe"><input name="id" type="hidden" value="123" /></form>
<iframe name="hideiframe" style='display:none'></iframe>
<script>
document.getElementById("csrffrm").submit();
</script>
然后狡猾的攻击者就可以通过各种方式吸引已经成功登陆www.a.com的用户点击进入www.b.com/csrfpage.aspx页面,最后恶意代码被执行,用户的博文ID为123的文章在不知不觉中被攻击者删除了
CSRF攻击的条件
根据上面的原理可以看出要实施CSRF攻击需要满足下面几个条件:
一、需要了解目标系统的目录和相关参数名称,其实要满足这个条件并不困难,攻击者通过相关的“系统目录彩虹表”进行检测又或者攻击者本身也是目标系统的用户之一,那么了解目标系统就更容易了;
二、需要一个执行恶意代码的网站,这个网站有可能是攻击者事先部署好的网站,或者恶意网站存在XSS漏洞刚好被攻击者利用;
三、需要目标系统的用户登录并且获得了合法的操作权限,同时用户被诱惑进入了恶意的网站;
要实施CSRF攻击要满足这三个条件,由于这些条件并不是那么容易被满足,所以比较容易被开发者所忽略。
CSRF攻击的防御策略
1.使用验证码
记得之前的12306网站上每次查票都要输入恶心的验证码,之所以要设计这个验证码它的目的是为了防止机器刷票,当然也能有效的预防CSRF攻击,但是如果每个操作都要用户输入验证码用户可能会崩溃掉,用户体验效果非常的不好;
2.检查Referer(来源)
除了验证码还可以检查Referer是否来自于同一个源,如果Referer是同源的那么这个操作是可信的,这个方法通常用于防止图片盗链,但是有些时候Referer并不是那么的可靠,服务器并不是能够百分之百的获得,比如如果用户启用了浏览器的隐私策略那么浏览器就有可能阻止发送Referer,服务器就有可能无法获取到这个值,所以这个方式不符合科学严谨的原则;
3.使用token(随机令牌)
服务器生成一个随机令牌,并保存起来,可以保存在服务端的session集合里边,或者保存在客户端的cookie、或者页面视图状态中都是可以的,由于浏览器的同源策略,恶意网站无法读取到目标网站的cookie和页面视图状态,然后把随机令牌随表单一起提交并在服务端验证随机令牌的有效性;
关于怎么实现随机令牌Token的防伪呢?
ASP.NET MVC 获取及手动更新AntiForgeryToken防伪标记
在ASP.NET MVC中是可以通过代码手动控制防伪标记(AntiForgeryToken
)的更新。另外在介绍更新令牌的具体方法前,会先说明如何获取防伪令牌,毕竟要以此为基础才能实现。
在ASP.NET MVC中,默认只要在视图页面中使用@Html.AntiForgeryToken()
,就会自动生成表单令牌和Cookie令牌。但是我们后台如果想要实现手动获取,就要使用System.Web.Helpers.AntiForgery
这个类,有兴趣查看源码的朋友会发现,其实@Html.AntiForgeryToken()
内部调用的方法和AntiForgery
这个类是一样的。
主要是通过AntiForgery.GetHtml()
或者AntiForgery.GetTokens(string oldCookieToken,out string newCookieToken,out string formToken)
这两个静态方法获取对应的表单和Cookie令牌。不过要注意的是,一旦调用了GetHtml方法,会自动生成对应的Cookie令牌,然后直接返回一段带有表单令牌值隐藏字段的Html代码,返回的值是这种形式的:
<input name="__RequestVerificationToken" type="hidden" value="8_nUk_3z0svQr9qcvRBi9SWMZ2-SYmuy9kRe9OgRobGULkb2Z4JZxRZFhR0ndeoy9hmDLDru7MFk-W4xrnL5z5T6VbkfXK7fyRk-egQBGm41">
隐藏字段的name名称一般固定为"__RequestVerificationToken",值是经过加密后的防伪令牌。 这个隐藏字段一般要放置在所需提交的表单中,最后提交的时候会和Cookie令牌进行对比验证。
如果使用GetTokens方法,传入对应的参数后可以获取加密后的表单和Cookie令牌,但是这里要自己存储对应的值。
接下来开始介绍手动更新AntiForgeryToken的方法,主要是通过AJAX进行操作。
方法一、使用GetHtml的方法获取表单令牌并更新
默认的View视图代码如下(有一个使用防伪标记的表单):
@using (Html.BeginForm("TokenTest", "Home", FormMethod.Post, new { id = "formLogin" }))
{<div id="divAntiForgeryToken">@Html.AntiForgeryToken()</div>//此处省略其他表单代码<input type="submit" value="提交" />
}
View中Js关键代码如下:
//注意引用jQuery库
//一定不能使用异步!async要设置成false
$.ajax({url: '/Home/RefreshToken', async: false, type: 'GET', success: function (data) { //方法一:直接更新HTML代码$("#divAntiForgeryToken").empty().append(data);//方法二:替换隐藏字段值//var tokenValue = $('<div />').html(data).find('input[type="hidden"]').val();//$('input[name="__RequestVerificationToken"]').val(tokenValue);}
});
这里需要注意,此处只列举出关键代码,具体在什么时候刷新令牌就要根据自己的需求来,比如点击某个按钮,或者表单提交的时候。另外之所以设置async: false
,是为了能保证令牌能进行同步更新,特别是与身份验证和授权有关的情况!
Controller控制台的令牌刷新操作方法代码如下:
public ActionResult RefreshToken()
{ var tokenHtml = System.Web.Helpers.AntiForgery.GetHtml().ToString();return Content(tokenHtml);
}
方法二,使用GetTokens方法灵活控制令牌
如果用这种方法,就要在后台多做点工作。View的代码和方法一一样就可以了,主要在于后台的控制器代码:
public ActionResult RefreshToken()
{string cookieToken;string formToken;System.Web.Helpers.AntiForgery.GetTokens(null, out cookieToken, out formToken);string cookieName = "__RequestVerificationToken";if (Response.Cookies.AllKeys.Contains(cookieName)){Response.Cookies[cookieName].Value = cookieToken;}else{Response.Cookies.Add(new HttpCookie(cookieName, cookieToken));}return Content(formToken);
}
View中的Js部分也要稍作修改:
$.ajax({url: '/Home/RefreshToken', async: false, type: 'GET', success: function (data) {$('input[name="__RequestVerificationToken"]').val(data);}
});
总结下,从源码上来看,其实GetHtml方法内部是调用了GetTokens方法的,所以两者并没有什么区别!从使用角度来看,方法一比较便捷,内部已经帮我们做好了相关处理,只需要在表单中放入对应的令牌即可。方法二还需要自己保存Cookie令牌,但是如果需要灵活操作就可以可考虑方法二。
另外上面的方法只是个思路,我还看过国外程序员通过加载PartialView来刷新防伪标识的,PartialView中只包含这段代码:@Html.AntiForgeryToken()
。其实思路大体上一样的,只是换成了另外一种形式。
转载:http://www.xjqyc.cn