简介

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

场景

某程序员大神God在某在线银行Online Bank给他的朋友Friend转账。

  

  转账后,出于好奇,大神God查看了网站的源文件,以及捕获到转账的请求。

  大神God发现,这个网站没有做防止CSRF的措施,而且他自己也有一个有一定访问量的网站,于是,他计划在自己的网站上内嵌一个隐藏的Iframe伪造请求(每10s发送一次),来等待鱼儿Fish上钩,给自己转账。

  网站源码:

<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=gb2312" />
<title></title>
</head>
<body>
<div>
我是一个内容丰富的网站,你不会关闭我!
</div> <iframe name="frame" src="invalid.html" sandbox="allow-same-origin allow-scripts allow-forms" style="display: none; width: 800px; height: 1000px;"> </iframe>
<script type="text/javascript">
setTimeout("self.location.reload();", 10000);
</script>
</body>
</html>

    伪造请求源码:

<html>
<head>
<title></title>
</head>
<body>
<form id="theForm" action="http://localhost:22699/Home/Transfer" method="post">
<input class="form-control" id="TargetUser" name="TargetUser" placeholder="用户名" type="text" value="God" />
<input class="form-control" id="Amount" name="Amount" placeholder="转账金额" type="text" value="100" />
</form> <script type="text/javascript">
document.getElementById('theForm').submit();
</script>
</body>
</html>

  鱼儿Fish打开了大神God的网站,在上面浏览丰富多彩的内容。此时伪造请求的结果是这样的(为了演示效果,去掉了隐藏):

  

  因为鱼儿Fish没有登陆,所以,伪造请求一直无法执行,一直跳转回登录页面。

  然后鱼儿Fish想起了要登录在线银行Online Bank查询内容,于是他登录了Online Bank。

  此时伪造请求的结果是这样的(为了演示效果,去掉了隐藏):

  鱼儿Fish每10秒会给大神God转账100元。

  

  

防止CSRF

  CSRF能成功是因为同一个浏览器会共享Cookies,也就是说,通过权限认证和验证是无法防止CSRF的。那么应该怎样防止CSRF呢?其实防止CSRF的方法很简单,只要确保请求是自己的站点发出的就可以了。那怎么确保请求是发自于自己的站点呢?ASP.NET以Token的形式来判断请求。

  我们需要在我们的页面生成一个Token,发请求的时候把Token带上。处理请求的时候需要验证Cookies+Token。

  

  此时伪造请求的结果是这样的(为了演示效果,去掉了隐藏):

$.ajax

  如果我的请求不是通过Form提交,而是通过Ajax来提交,会怎样呢?结果是验证不通过。

  为什么会这样子?我们回头看看加了@Html.AntiForgeryToken()后页面和请求的变化。

  1. 页面多了一个隐藏域,name为__RequestVerificationToken。

  2. 请求中也多了一个字段__RequestVerificationToken。

  

  原来要加这么个字段,我也加一个不就可以了!

  啊!为什么还是不行...逼我放大招,研究源码去!

  

  噢!原来token要从Form里面取。但是ajax中,Form里面并没有东西。那token怎么办呢?我把token放到碗里,不对,是放到header里。

  js代码:

$(function () {
var token = $('@Html.AntiForgeryToken()').val(); $('#btnSubmit').click(function () {
var targetUser = $('#TargetUser').val();
var amount = $('#Amount').val();
var data = { 'targetUser': targetUser, 'amount': amount };
return $.ajax({
url: '@Url.Action("Transfer2", "Home")',
type: 'POST',
data: JSON.stringify(data),
contentType: 'application/json',
dataType: 'json',
traditional: 'true',
beforeSend: function (xhr) {
xhr.setRequestHeader('__RequestVerificationToken', token);
},
success:function() {
window.location = '@Url.Action("Index", "Home")';
}
});
});
});

  在服务端,参考ValidateAntiForgeryTokenAttribute,编写一个AjaxValidateAntiForgeryTokenAttribute:

    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class AjaxValidateAntiForgeryTokenAttribute : FilterAttribute, IAuthorizationFilter
{
public void OnAuthorization(AuthorizationContext filterContext)
{
if (filterContext == null)
{
throw new ArgumentNullException("filterContext");
} var request = filterContext.HttpContext.Request; var antiForgeryCookie = request.Cookies[AntiForgeryConfig.CookieName];
var cookieValue = antiForgeryCookie != null ? antiForgeryCookie.Value : null;
var formToken = request.Headers["__RequestVerificationToken"];
AntiForgery.Validate(cookieValue, formToken);
}
}

  然后调用时把ValidateAntiForgeryToken替换成AjaxValidateAntiForgeryToken。

  

  大功告成,好有成就感!

全局处理

  如果所有的操作请求都要加一个ValidateAntiForgeryToken或者AjaxValidateAntiForgeryToken,不是挺麻烦吗?可以在某个地方统一处理吗?答案是阔仪的。

  ValidateAntiForgeryTokenAttribute继承IAuthorizationFilter,那就在AuthorizeAttribute里做统一处理吧。

  ExtendedAuthorizeAttribute:

    public class ExtendedAuthorizeAttribute : AuthorizeAttribute
{
public override void OnAuthorization(AuthorizationContext filterContext)
{
PreventCsrf(filterContext);
base.OnAuthorization(filterContext);
GenerateUserContext(filterContext);
} /// <summary>
/// http://www.asp.net/mvc/overview/security/xsrfcsrf-prevention-in-aspnet-mvc-and-web-pages
/// </summary>
private static void PreventCsrf(AuthorizationContext filterContext)
{
var request = filterContext.HttpContext.Request; if (request.HttpMethod.ToUpper() != "POST")
{
return;
} var allowAnonymous = HasAttribute(filterContext, typeof(AllowAnonymousAttribute)); if (allowAnonymous)
{
return;
} var bypass = HasAttribute(filterContext, typeof(BypassCsrfValidationAttribute)); if (bypass)
{
return;
} if (filterContext.HttpContext.Request.IsAjaxRequest())
{
var antiForgeryCookie = request.Cookies[AntiForgeryConfig.CookieName];
var cookieValue = antiForgeryCookie != null ? antiForgeryCookie.Value : null;
var formToken = request.Headers["__RequestVerificationToken"];
AntiForgery.Validate(cookieValue, formToken);
}
else
{
AntiForgery.Validate();
}
} private static bool HasAttribute(AuthorizationContext filterContext, Type attributeType)
{
return filterContext.ActionDescriptor.IsDefined(attributeType, true) ||
filterContext.ActionDescriptor.ControllerDescriptor.IsDefined(attributeType, true);
} private static void GenerateUserContext(AuthorizationContext filterContext)
{
var formsIdentity = filterContext.HttpContext.User.Identity as FormsIdentity; if (formsIdentity == null || string.IsNullOrWhiteSpace(formsIdentity.Name))
{
UserContext.Current = null;
return;
} UserContext.Current = new WebUserContext(formsIdentity.Name);
}
}

  

  然后在FilterConfig注册一下。

  

  FAQ:

  1. BypassCsrfValidationAttribute是什么鬼?不是有个AllowAnonymousAttribute吗?

  如果有些操作你不需要做CSRF的处理,比如附件上传,你可以在对应的Controller或Action上添加BypassCsrfValidationAttribute。

  AllowAnonymousAttribute不仅会绕过CSRF的处理,还会绕过认证和验证。BypassCsrfValidationAttribute绕过CSRF但不绕过认证和验证,

也就是BypassCsrfValidationAttribute作用于那些登录或授权后的Action。

  2. 为什么只处理POST请求?

  我开发的时候有一个原则,查询都用GET,操作用POST,而对于查询的请求没有必要做CSRF的处理。大家可以按自己的需要去安排!

  

  3. 我做了全局处理,然后还在Controller或Action上加了ValidateAntiForgeryToken或者AjaxValidateAntiForgeryToken,会冲突吗?

  不会冲突,只是验证会做两次。

源码下载

  为了方便使用,我没有使用任何数据库,而是用了一个文件来存储数据。代码下载后可以直接运行,无需配置。

  下载地址:https://github.com/ErikXu/CSRF

Web安全相关(二):跨站请求伪造(CSRF/XSRF)的更多相关文章

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

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

  2. 跨站请求伪造 CSRF / XSRF<二:应用>

    防御的方法主要有两种<java示例> 1.检查Referer字段 HTTP头中有一个Referer字段,这个字段用以标明请求来源于哪个地址.在处理敏感数据请求时,通常来说,Referer字 ...

  3. django之跨站请求伪造csrf

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

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

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

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

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

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

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

  7. .NET Core实战项目之CMS 第十四章 开发篇-防止跨站请求伪造(XSRF/CSRF)攻击处理

    通过 ASP.NET Core,开发者可轻松配置和管理其应用的安全性. ASP.NET Core 中包含管理身份验证.授权.数据保护.SSL 强制.应用机密.请求防伪保护及 CORS 管理等等安全方面 ...

  8. Web安全测试之跨站请求伪造(CSRF)篇

    跨站请求伪造(即CSRF)被Web安全界称为诸多漏洞中“沉睡的巨人”,其威胁程度由此“美誉”便可见一斑.本文将简单介绍该漏洞,并详细说明造成这种漏洞的原因所在,以及针对该漏洞的黑盒测试与灰盒子测试具体 ...

  9. ASP.NET Core 防止跨站请求伪造(XSRF/CSRF)攻击 (转载)

    什么是反伪造攻击? 跨站点请求伪造(也称为XSRF或CSRF,发音为see-surf)是对Web托管应用程序的攻击,因为恶意网站可能会影响客户端浏览器和浏览器信任网站之间的交互.这种攻击是完全有可能的 ...

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

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

随机推荐

  1. TypeScript为Zepto编写LazyLoad插件

    平时项目中使用的全部是jQuery框架,但是对于做webapp来说jQuery太过于庞大,当然你可以选择jQuery 2.*针对移动端的版本. 这里我采用移动端使用率比较多的zepto框架,他跟jqu ...

  2. MySQL 系列(一) 生产标准线上环境安装配置案例及棘手问题解决

    一.简介 MySQL是最流行的开放源码SQL数据库管理系统,它是由MySQL AB公司开发.发布并支持的.有以下特点: MySQL是一种数据库管理系统. MySQL是一种关联数据库管理系统. MySQ ...

  3. Oracle 数据库语句大全

    Oracle数据库语句大全 ORACLE支持五种类型的完整性约束 NOT NULL (非空)--防止NULL值进入指定的列,在单列基础上定义,默认情况下,ORACLE允许在任何列中有NULL值. CH ...

  4. 代码的坏味道(17)——夸夸其谈未来性(Speculative Generality)

    坏味道--夸夸其谈未来性(Speculative Generality) 特征 存在未被使用的类.函数.字段或参数. 问题原因 有时,代码仅仅为了支持未来的特性而产生,然而却一直未实现.结果,代码变得 ...

  5. C#——传值参数(1)

    //我的C#是跟着猛哥(刘铁猛)(算是我的正式老师)<C#语言入门详解>学习的,微信上猛哥也给我讲解了一些不懂得地方,对于我来说简直是一笔巨额财富,难得良师! 这次与大家一起学习C#中的值 ...

  6. IdentityServer4 使用OpenID Connect添加用户身份验证

    使用IdentityServer4 实现OpenID Connect服务端,添加用户身份验证.客户端调用,实现授权. IdentityServer4 目前已更新至1.0 版,在之前的文章中有所介绍.I ...

  7. ActionContext.getContext().getSession()

    ActionContext.getContext().getSession() 获取的是session,然后用put存入相应的值,只要在session有效状态下,这个值一直可用 ActionConte ...

  8. Javascript学习笔记

    Javascript 2016年12月19日整理 JS基础 Chapter1 JS是一门运行在浏览器客户端的脚本编程语言,前台语言 组成部分 1. ECMAscript JS标准 2. DOM 通过J ...

  9. vs生成pro

    1.修改.vcxproj文件   <PropertyGroup Label="Globals">    <ProjectGuid>{AAAA4039-13B ...

  10. Ubuntu14.04配置Mono+Jexus

    总所周知,ASP.NET是微软公司的一项技术,是一个网站服务端开发的一种技术,它可以在通过HTTP请求文档时再在Web服务器上动态创建它们,就是所谓动态网站开发,它依赖运行于 IIS 之中的程序 .但 ...