Web安全相关(二):跨站请求伪造(CSRF/XSRF)
简介
场景
某程序员大神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,会冲突吗?
不会冲突,只是验证会做两次。
源码下载
为了方便使用,我没有使用任何数据库,而是用了一个文件来存储数据。代码下载后可以直接运行,无需配置。
Web安全相关(二):跨站请求伪造(CSRF/XSRF)的更多相关文章
- 跨站请求伪造 CSRF / XSRF<一:介绍>
跨站请求伪造(英语:Cross-site request forgery),也被称为 one-click attack 或者 session riding,通常缩写为 CSRF 或者 XSRF, 是一 ...
- 跨站请求伪造 CSRF / XSRF<二:应用>
防御的方法主要有两种<java示例> 1.检查Referer字段 HTTP头中有一个Referer字段,这个字段用以标明请求来源于哪个地址.在处理敏感数据请求时,通常来说,Referer字 ...
- django之跨站请求伪造csrf
目录 跨站请求伪造 csrf 钓鱼网站 模拟实现 针对form表单 ajax请求 csrf相关的两个装饰器 跨站请求伪造 csrf 钓鱼网站 就类似于你搭建了一个跟银行一模一样的web页面 , 用户在 ...
- 跨站请求伪造(CSRF)-简述
跨站请求伪造(CSRF)-简述 跨站请求伪造(英语:Cross-site request forgery),也被称为 one-click attack 或者 session riding,通常缩写为 ...
- 跨站请求伪造(CSRF)攻击原理解析:比你所想的更危险
跨站请求伪造(CSRF)攻击原理解析:比你所想的更危险 跨站请求伪造(Cross-Site Request Forgery)或许是最令人难以理解的一种攻击方式了,但也正因如此,它的危险性也被人们所低估 ...
- PHP安全编程:跨站请求伪造CSRF的防御(转)
跨站请求伪造(CSRF)是一种允许攻击者通过受害者发送任意HTTP请求的一类攻击方法.此处所指的受害者是一个不知情的同谋,所有的伪造请求都由他发起,而不是攻击者.这样,很你就很难确定哪些请求是属于跨站 ...
- .NET Core实战项目之CMS 第十四章 开发篇-防止跨站请求伪造(XSRF/CSRF)攻击处理
通过 ASP.NET Core,开发者可轻松配置和管理其应用的安全性. ASP.NET Core 中包含管理身份验证.授权.数据保护.SSL 强制.应用机密.请求防伪保护及 CORS 管理等等安全方面 ...
- Web安全测试之跨站请求伪造(CSRF)篇
跨站请求伪造(即CSRF)被Web安全界称为诸多漏洞中“沉睡的巨人”,其威胁程度由此“美誉”便可见一斑.本文将简单介绍该漏洞,并详细说明造成这种漏洞的原因所在,以及针对该漏洞的黑盒测试与灰盒子测试具体 ...
- ASP.NET Core 防止跨站请求伪造(XSRF/CSRF)攻击 (转载)
什么是反伪造攻击? 跨站点请求伪造(也称为XSRF或CSRF,发音为see-surf)是对Web托管应用程序的攻击,因为恶意网站可能会影响客户端浏览器和浏览器信任网站之间的交互.这种攻击是完全有可能的 ...
- 跨站请求伪造(csrf)中间件整理
一. CSRF中间件 字面意思跨站请求伪造; 即模仿个请求朝服务器发送,django中对跨站伪造的请求有相应的校验 from django.views.decorators.csrf import c ...
随机推荐
- 用CIL写程序:你好,沃尔德
前言: 项目紧赶慢赶总算在年前有了一些成绩,所以沉寂了几周之后,小匹夫也终于有时间写点东西了.以前匹夫写过一篇文章,对CIL做了一个简单地介绍,不过不知道各位看官看的是否过瘾,至少小匹夫觉得很不过瘾. ...
- Objective-C三种定时器CADisplayLink / NSTimer / GCD的使用
OC中的三种定时器:CADisplayLink.NSTimer.GCD 我们先来看看CADiskplayLink, 点进头文件里面看看, 用注释来说明下 @interface CADisplayLin ...
- 使用Oracle官方巡检工具ORAchk巡检数据库
ORAchk概述 ORAchk是Oracle官方出品的Oracle产品健康检查工具,可以从MOS(My Oracle Support)网站上下载,免费使用.这个工具可以检查Oracle数据库,Gold ...
- 谈谈一些有趣的CSS题目(九)-- 巧妙的实现 CSS 斜线
开本系列,谈谈一些有趣的 CSS 题目,题目类型天马行空,想到什么说什么,不仅为了拓宽一下解决问题的思路,更涉及一些容易忽视的 CSS 细节. 解题不考虑兼容性,题目天马行空,想到什么说什么,如果解题 ...
- PHP获取上个月最后一天的一个容易忽略的问题
正常来说,PHP是有一个很方便的函数可以获取上个月时间的 strtotime (PHP 4, PHP 5, PHP 7) strtotime - 将任何英文文本的日期时间描述解析为 Unix 时间戳 ...
- XAMARIN ANDROID 二维码扫描示例
现在二维码的应用越来越普及,二维码扫描也成为手机应用程序的必备功能了.本文将基于 Xamarin.Android 平台使用 ZXing.Net.Mobile 做一个简单的 Android 条码扫描示 ...
- 安装并使用PHPunit
安装并使用PHPunit Linux 下安装PHPunit PHP 档案包 (PHAR) 要获取 PHPUnit,最简单的方法是下载 PHPUnit 的 PHP 档案包 (PHAR),它将 PHPU ...
- 【python之路3】if 语句
1.if语句用法(if....else....) #!/usr/bin/env python # -*- coding:utf-8 -*- my_name = raw_input("plea ...
- Xamarin.Android广播接收器与绑定服务
一.前言 学习了前面的活动与服务后,你会发现服务对于活动而言似乎就是透明的,相反活动对于服务也是透明的,所以我们还需要一中机制能够将服务和活动之间架起一座桥梁,通过本节的学习,你将会学到广播与绑定服务 ...
- Lesson 14 Do you speak English?
Text I had an amusing experience last year. After I had left a small village in the south of France. ...