1. 什么是跨站请求伪造(CSRF)

  CSRF(Cross-site request forgery跨站请求伪造,也被称为“One Click Attack”或者session Riding,通常缩写为CSRF或者XSRF,是一种对网站的恶意利用。尽管听起来像跨站脚本(XSS),但它与XSS非常不同,并且攻击方式几乎相左。XSS利用站点内的信任用户,而CSRF则通过伪装来自受信任用户的请求来利用受信任的网站。与XSS攻击相比,CSRF攻击往往不大流行(因此对其进行防范的资源也相当稀少)和难以防范,所以被认为比XSS更具危险性。

  以上是来自百度百科的概念。下面各自举个简单的例子说明:

  XSS:假设没有预防XSS,我在文章评论区域输入:<script>while(1)alert("呵呵")</script> 并且成功提交。那么下次进来这个页面进来,就会不断弹出对话框,导致该页面无法被正常浏览;对此我们在输出内容时,如果该内容是用户输入的,那么就应该进行Html Encode,如上面的脚本就会以普通文本的形式显示出来。不仅如此,攻击脚本还可以读取用户cookie信息,做任何脚本能做的事情。

  CSRF:与XSS不同,CSRF利用的当前信任用户,让用户不知不觉的“自己提交”数据。例如用户 B 在用户 A 的文章评论区域上传一张图片,如:

[img]http://images2015.cnblogs.com/blog/798800/201512/798800-20151230205214839-1087717627.jpg[/img]

然后把它改成:

[img]http://Another/Forgery.html[/img]

这是一张无效的图片,并且来自于另一个站点。在 Forgery.html 中,伪造者创建一个表单,action指向要攻击的页面,当window.onload 执行时自动提交表单。

那么用户 A 在访问该页面时,就会发起请求到Another/Forgery.html。由于Web的身份验证信息通常会保存在浏览器cookie中,而cookie每次都会随请求提交到服务器,所以这个请求会把cookie 和 Forgery.html 的表单信息一起提交到服务器。服务器对Cookie进行相关验证,并且认为这是一个正常的请求,执行相关操作。

2. 模拟一次攻击

按照上面的思路,接下来我们模拟一次攻击。新建一个mvc项目,主要有两个页面,一个页面用于显示用户姓名和评论,另一个页面用于用户自己修改姓名。只是为了演示,这里我们固定一个用户:张三。如:  

    public class CurrentUser
{
PRivate static CurrentUser currentUser = new CurrentUser(){Name="张三"};
public string Name{get;set;}
public static CurrentUser Current
{
get
{
return currentUser;
}
}
}

  显示页面为:

  

  本站点的另一个页面用于修改用户姓名:  

    <div>
修改用户信息:
</div>
<form action="/home/update" method="post">
<p>
<label>用户名:</label>
<input name="name" />
</p>
<p>
<input type="submit" />
</p>
</form>

  

  对应Action为Update,为了提高安全性,我们给它标记一个[HttpPost]特性(实际情况这里会进行身份验证,然后根据用户id去修改信息)。如:

        [HttpPost]
public ActionResult Update(string name)
{
CurrentUser.Current.Name = name;
return RedirectToAction("Index");
}

  可以看到上面的评论区域有一个链接,来自另一个站点,它的代码很简单,与我们的修改页面类似,如下:

<body>
<div>
<form id="form" action="http://localhost:50025/home/update" method="post">
<input type="hidden" name="name" value="2b" />
</form>
</div>
</body>
<script type="text/javascript">
window.onload = function () {
document.getElementById("form").submit();
}
</script>

  可以看到,该页面表达的action指向了前面的站点的修改页面,并且在页面load完后,就会自动提交。当张三点击这个链接后,会发生什么呢?如下:

3. 如何防止

3.1 尽早防范

  永远不要相信用户提交的数据。通常我们会在前台和后台对用户的输入进行验证,确保数据的正确性和安全性。以上面的例子,如果用户上传一张图片,然后修改成.html的格式,那么应该不让它保存。我们可以看博客园的例子,如果这样做,可以保存,但显示出来就是普通文本的格式,这样页面加载时就不会对这个url发起请求。再看csdn,则会弹出提示非法输入。

  cnblogs:  

  csdn:   

  当然,这样只是第一道屏障。很多网站也可能像上面可以输入外部链接,如果用户去点击,依然可能被攻击。所以我们还需要进一步防范。

3.2 MVC 的做法

  前面我们模拟了CSRF的过程,接下看MVC里如何应对这种情况。

  ValidateAntiForgeryAttribute特性

  这是一个继承了FilterAttribute 和 实现了 IActionFilter 的标记特性,它可以应用在Controller或者Action上面。我们知道实现IActionFilter的 Filter会在 Action执行前进行相关处理,具体逻辑在 IActionFilter接口的 OnAuthorization 方法中。MVC 就是在这个方法中进行验证的。具体是如何验证的呢?

  AntiForgeryToken方法

  ValidateAntiForgery特性表示操作需要验证,我们还需要使用HtmlHelper的 AntiForgeryToken方法,这是一个实例方法。具体是在View的表单里调用该方法,该方法会生成一个name为__RequestVerificationToken的 input hidden标签,值就是防伪令牌。除此之外,还会生成一个同样名称并且标记为HttpOnly的cookie,值也是通过加密生成的防伪令牌。ValidateAntiForgery特性的OnAuthorization方法就是根据这两个进行验证的。具体是:

  1. 用户请求该页面,AntiForgeryToken方法会生成一个input hidden 和 cookie,值都是经过加密处理的Token。

  2. 用户提交请求,如果Action(Controller)标记了 ValidateAntiForgery特性,则进行验证。

      2.1 如果表单没有一个name为 __RequestVerificationToken的元素,则抛出HttpAntiForgeryException。

    2.2 如果没有一个name为__RequestVerificationToken的cookie,则抛出HttpAntiForgeryException。

    2.3 解析input 和 cookie 的值,判断是否匹配(包括用户名、时间等的比较),不匹配则抛出HttpAntiForgeryException。

  3. 接收到异常,显示错误页或抛出黄页。

  至于 input 的值 和 cookie 的生成,mvc内部会根据当前用户名,时间以及集合 MachineKey 等去加密生成,确保不会轻易被猜出。有兴趣的朋友可以通过源码了解详细过程。

  按照上面的做法,我们给Update加上一个[ValidateAntiForgery]特性,并且在表单调用HtmlHelper的AntiForgeryToken方法。此时如果用户点击链接,一样访问了Forgery.html,并且自动提交表单,cookie还是一样会提交,但伪造页面无法知道input hidden 的值,所以无法通过验证。

3.3 WebForm 的做法

  WebForm 没有 AntiForgeryToken方法 可以直接使用,不过知道MVC的实现过程后,我们也可以自己实现一套。

  在页面表单,像mvc一样,我们也输出一个名称为:_RequestVerificationToken 的 input hidden 标签,值为序列化后的Token,具体是调用 HttpRespose 的扩展方法AntiForgeryToken。AntiForgeryToken方法不仅会输入input hidden,还会将Guid存储在Context.Item,这是一个在一次请求内各个时期可以使用的集合,在页面周期完成后,我们判断是否有这个标记,如果有,还需要将它写入到Cookie当中。

  表单:

    <form id="Form1" action="UpdateAntiCsrf.aspx" method="post" runat="server">
<div>
<%=Response.AntiForgeryToken() %>
<input type="text" name="name"/>
<input type="submit"/>
</div>
</form>

  AntiForgeryToken扩展方法:

    public static class HttpResposeExtentions
{
public static string AntiForgeryToken(this HttpResponse response)
{
HttpContext context = HttpContext.Current;
if (context == null)
{
throw new InvalidOperationException("无效请求!");
}
Guid guid = Guid.NewGuid();
context.Items["_RequestVerificationToken"] = guid;
ObjectStateFormatter formatter = new ObjectStateFormatter();
return string.Format("<input type='hidden' name='_RequestVerificationToken' value={0} />", formatter.Serialize(guid));
}
}

  对于验证Token,和将GUID写入到Cookie是通过一个AntiCsrfModule完成的,它主要拦截页面执行前和执行后两个事件。页面执行后完成上面是否需要将GUID写入Cookie的判断,而页面执行前则判断是否需要验证,以及验证结果,一旦不匹配,就抛出异常。代码如下:

    public class AntiCsrfModule : IHttpModule
{
public void Dispose ()
{
} public void Init(Httpapplication app)
{
app.PreRequestHandlerExecute += new EventHandler(app_PreRequestHandlerExecute);
app.PostRequestHandlerExecute += new EventHandler(app_PostRequestHandlerExecute);
} void app_PreRequestHandlerExecute(object sender, EventArgs e)
{
HttpContext context = ((HttpApplication)sender).Context;
HttpRequest request = context.Request;
IHttpHandler handler = context.Handler;
if (handler.GetType().IsDefined(typeof(ValidationAntiForgeryAttribute), true))
{
if (request.HttpMethod.Equals("POST", StringComparison.CurrentCultureIgnoreCase))
{
HttpCookie cookie = request.Cookies["_RequestVerificationToken"];
if (cookie == null)
{
throw new InvalidOperationException("无效请求!");
}
string value = request.Form["_RequestVerificationToken"];
if (string.IsNullOrEmpty(value))
{
throw new InvalidOperationException("无效请求!");
}
ObjectStateFormatter formatter = new ObjectStateFormatter();
Guid? guid = formatter.Deserialize(value) as Guid?;
if(guid.HasValue && guid.Value.ToString() == cookie.Value)
{
return;
}
throw new InvalidOperationException("无效请求!");
}
}
} void app_PostRequestHandlerExecute(object sender, EventArgs e)
{
HttpContext context = ((HttpApplication)sender).Context;
Guid? guid = context.Items["_RequestVerificationToken"] as Guid?;
if (guid.HasValue)
{
HttpCookie cookie = new HttpCookie("_RequestVerificationToken", guid.Value.ToString());
cookie.HttpOnly = false;
context.Response.Cookies.Add(cookie);
}
}
}  

  对于需要验证的页面,通过一个ValidateAntiForgeryAttribute特性标记,如下:

    public class ValidateAntiForgeryAttribute : Attribute
{
}

  同样,我们像前面一样模拟一次攻击。结果如我们所想,会抛出黄页。

  

3.4 Ajax 方式

  上面我们都是通过Post 表单的形式提交数据,如果是以ajax提交的呢?我们可以在后台判断请求是否是Ajax请求,如果不是则不允许操作。因为js受同源策略限制,另一个域在没有被授权的情况下,脚本是无法和本域进行通信的。也就是Another/Forgery.html可以以post的形式提交数据到我们后台,但没办法以ajax的形式提交,也没办法调用我们页面的方法或者访问dom元素。

4. 博客园的实现

  例子就在身边。我们看到博客园【设置基本资料】模块,查看源码就会发现这里用用到了这个技术。

  表单:

  

  Cookie:

原文转至:http://www.knowsky.com/897427.html

跨站请求伪造(CSRF)的更多相关文章

  1. PHP安全编程:跨站请求伪造CSRF的防御(转)

    跨站请求伪造(CSRF)是一种允许攻击者通过受害者发送任意HTTP请求的一类攻击方法.此处所指的受害者是一个不知情的同谋,所有的伪造请求都由他发起,而不是攻击者.这样,很你就很难确定哪些请求是属于跨站 ...

  2. 跨站请求伪造(CSRF)-简述

    跨站请求伪造(CSRF)-简述 跨站请求伪造(英语:Cross-site request forgery),也被称为 one-click attack 或者 session riding,通常缩写为 ...

  3. 跨站请求伪造(CSRF)攻击原理解析:比你所想的更危险

    跨站请求伪造(CSRF)攻击原理解析:比你所想的更危险 跨站请求伪造(Cross-Site Request Forgery)或许是最令人难以理解的一种攻击方式了,但也正因如此,它的危险性也被人们所低估 ...

  4. django之跨站请求伪造csrf

    目录 跨站请求伪造 csrf 钓鱼网站 模拟实现 针对form表单 ajax请求 csrf相关的两个装饰器 跨站请求伪造 csrf 钓鱼网站 就类似于你搭建了一个跟银行一模一样的web页面 , 用户在 ...

  5. 跨站请求伪造 CSRF / XSRF<一:介绍>

    跨站请求伪造(英语:Cross-site request forgery),也被称为 one-click attack 或者 session riding,通常缩写为 CSRF 或者 XSRF, 是一 ...

  6. 跨站请求伪造CSRF(Cross-site request forgery)

    CSRF(Cross-site request forgery)跨站请求伪造,也被称为“One Click Attack”或者Session Riding,通常缩写为CSRF或者XSRF,是一种对网站 ...

  7. 跨站请求伪造(csrf)中间件整理

    一. CSRF中间件 字面意思跨站请求伪造; 即模仿个请求朝服务器发送,django中对跨站伪造的请求有相应的校验 from django.views.decorators.csrf import c ...

  8. 跨站请求伪造(csrf)的防护手段

    CSRF CSRF全拼为Cross Site Request Forgery,译为跨站请求伪造. CSRF指攻击者盗用了你的身份,以你的名义发送恶意请求. 造成的问题:个人隐私泄露以及财产安全. CS ...

  9. 跨站请求伪造CSRF:攻击与防御

    CSRF是什么         (Cross Site Request Forgery, 跨站域请求伪造)是一种网络的攻击方式,它在 2007 年曾被列为互联网 20 大安全隐患之一,也被称为&quo ...

  10. 跨站请求伪造CSRF

    CSRF是Cross Site Request Forgery的缩写,乍一看和XSS差不多的样子,但是其原理正好相反,XSS是利用合法用户获取其信息,而CSRF是伪造成合法用户发起请求. 在XSS危害 ...

随机推荐

  1. 爬虫--反爬--css反爬---大众点评爬虫

    大众点评爬虫分析,,大众点评 的爬虫价格利用css的矢量图偏移,进行加密 只要拦截了css 解析以后再写即可 # -*- coding: utf- -*- """ Cre ...

  2. Python函数学习——匿名函数

    匿名函数 匿名函数就是不需要显式的指定函数名. 关键字lambda表示匿名函数,冒号前面的n表示函数参数,可以有多个参数. 匿名函数有个限制,就是只能有一个表达式,不用写return,返回值就是该表达 ...

  3. Jenkins系列之三——centos7.4+GitLab+Jenkins部署

    GitLab介绍 GitLab 是一个用于仓库管理系统的开源项目,使用Git作为代码管理工具,并在此基础上搭建起来的web服务. 系统环境准备 建议:内存4G以上不然带不动 [root@hejianl ...

  4. 字符串replaceAll()方法报错:java.util.regex.PatternSyntaxException:Unclosed group near index...

    Unclosed group near index错误 之前自己写的一个模块在项目上线后最近频频出现tomcat崩掉的情况,从log看出现如下的报错信息: java.util.regex.Patter ...

  5. 深入学习卷积神经网络(CNN)的原理知识

    网上关于卷积神经网络的相关知识以及数不胜数,所以本文在学习了前人的博客和知乎,在别人博客的基础上整理的知识点,便于自己理解,以后复习也可以常看看,但是如果侵犯到哪位大神的权利,请联系小编,谢谢.好了下 ...

  6. vuejs深入浅出—基础篇

    一.从HelloWorld说起 任何语言的都是从Hello World开始的,VueJs也不例外,直接上代码: <script src="https://unpkg.com/vue/d ...

  7. ThreadLocalMap里Entry为何声明为WeakReference?

    Java里,每个线程都有自己的ThreadLocalMap,里边存着自己私有的对象.Map的Entry里,key为ThreadLocal对象,value即为私有对象T.在spring MVC中,常用T ...

  8. #9 Python列表和元组

    前言 Python中有6种序列:列表.元组.字符串.Unicode字符串.buffer对象和xrange对象.序列通用操作包括:索引.切片.长度.加.乘.最大值.最小值,遍历和检查成员.虽然Pytho ...

  9. xshell远程终端操作Ubuntu server安装LAMP环境之最详细笔记之二PHP开发环境配置

    前言: 昨天学会了安装server,今天试着通过远程终端xshell来安装LAMP,搭配一下开发环境,也有集成环境可以一键安装使用,还是瞎折腾一下,手动一步一步搭建一下这个开发环境. 接上一篇:ubu ...

  10. 使用Asp.Net Core MVC 开发项目实践[第五篇:缓存的使用]

    项目中我们常常会碰到一些数据,需要高频率用到但是又不会频繁变动的这类,我们就可以使用缓存把这些数据缓存起来(比如说本项目的导航数据,帖子频道数据). 我们项目中常用到有Asp.Net Core 本身提 ...