CSRF/XSRF保护
"跨站请求伪造(CSRF)是网络攻击的一种类型,当一个恶意的网站、email、博客、即时通讯或程序导致用户的浏览器在一个用户授权的网站上执行了一个不需要的action时发生"(OWASP)。
在这里也有简单的介绍解释了如何在ASP.NET Web API上实现这种攻击。
ABP框架简化并自动防范CSRF攻击。启动模板里包含相关的预配置。在本文档中,将解释保护机制是如何集成到ABP平台及如何工作的。
GET,HEAD,OPTIONS和TRACE这几个 HTTP动词不需要保护,因为通常他们是没有副作用的(不改变数据库)。ABP只为POST、PUT、PATCH和DELETE动词实现了自动伪造保护,你可以使用在本文档中定义的特性来改变这种行为。
CSRF是一种因为浏览器的问题造成的攻击类型。因为浏览器为所有的请求发送所有的cookieds(包含授权的cookies),包含跨域请求。但是,非浏览器客户端不存在这个问题,如移动应用。ABP框架可以分辨不同的客户端,自动为非浏览器客户端跳过防伪造校验。
ASP.NET MVC有自己內建的防伪造系统。但是它有一些弱点:
- 需要给所有需要保护的actions添加ValidateAntiForgeryToken特性。我们有可能忘记给所有需要的actions添加这个特性。
- 在HTML表单字段中, ValidateAntiForgeryToken特性只检查_RequestVerificationToken。这样就很难或不可能在AJAX请求中使用,尤其是当content-type类型为"application/json"时。在AJAX请求中,通常会在请求报头中设置token。
- 在javascript代码中很难验证token(尤其是如果你编写的javascript代码没有在.cshtml文件中).我们需要使用AJAX请求访问它。
- 即使我们能够在javascript中访问token,我们需要在每次请求的报头中添加它。
ABP采取了下面的措施来克服这些困难:
- 不再需要为POST、PUT、PATCH和DELETE actions添加ValidateAntiFogeryTokey特性,因为他们是自动保护的(通过AbpAntiForgeryMvcFilter)。自动保护足以应付大多数情况。但是,你可以使用DisableAbpAniForgeryTokeyValidation特性来为action或controller禁用它,也可以使用ValidateAbpAntiForgeryTokey特性为特定的action/controller启用它。
- 除了HTML表单字段,AbpAntiForgeryMvcFilter也检查报头中的token。因此,我们可以很容易的在AJAX请求中使用防伪造token保护。
- 提供abp.security.antiForgery.getToken()方法来在javascript中获取token,即使你并不需要它。
- 自动为所有的AJAX请求报头添加方伪造token。
因此,它几乎无缝的工作。
启动模板已经集成了CSRF保护。如果你需要手动在工程中添加(可能你的工程是在我们添加它之前创建的),跟随这个向导。
我们需要在Layout视图中添加下面的代码:
@{
SetAntiForgeryCookie();
}
因此,所有包含他的页都会使用这个布局。这个方法定义在ABP基础视图类中。它创建、设置合适的token cookies,使javascript可以实现这个功能。如果你有多个布局,那么每个布局都需要添加这段代码。
对于ASP.NET MVC应用,这就是所有我们需要做的。所有的AJAX请求会自动完成。但是,对于HTML表单,我们仍然需要使用@Html.AntiForgeryToken()HTML助手,因为它不通过AJAX请求(但是不需要为相应的action添加ValidateAbpAntiForgeryToken特性)。
XSRF保护默认是启用的。你可以在模块的PreInitialize方法中禁用或配置。示例:
Configuration.Modules.AbpWeb().AntiForgery.IsEnabled = false;
你也可以使用Configuration.Modules.AbpWebCommon().AntiForgery对象配置token和cookie的名字。
ASP.NET Web API没有方伪造机制。ABP提供了基础设施给ASP.NET Web API控制器自动添加CSRF保护。
如果你在MVC工程中使用Web API,不需要额外的配置。即使你在其他进程中自己寄宿你的Web API层,只要你在一个配置的MVC应用中发起AJAX请求,就不需要配置。
如果你的客户端是不同种类的应用(比如说,一个独立的angularjs应用,不能使用之前描述的SetAntiForgeryCookie()方法),那么你需要提供一种设置防伪造token cookie的方式。一种可能的方式是创建一个api控制器,如下:
using System.Net.Http; using Abp.Web.Security.AntiForgery; using Abp.WebApi.Controllers; namespace AngularForgeryDemo.Controllers { public class AntiForgeryController : AbpApiController { private readonly IAbpAntiForgeryManager _antiForgeryManager; public AntiForgeryController(IAbpAntiForgeryManager antiForgeryManager) { _antiForgeryManager = antiForgeryManager; } public HttpResponseMessage GetTokenCookie() { var response = new HttpResponseMessage(); _antiForgeryManager.SetCookie(response.Headers); return response; } } }
然后,你可以在客户端调用这个action来设置cookie。
ASP.NET Core MVC相比之前的版本(ASP.NET MVC 5.x)有更好的方伪造机制:
- 它有AutoValidateAntiforgeryTokenAttribute类,这个类可以为所有的POST、PUT、PATCH和DLETE actions自动进行防伪造校验。
- 它有ValidateAntiForgeryToken和IgnoreAntiforgeryToken特性,用来控制token校验。
- 如果没有显示禁用它,会自动为HTML表单添加方伪造安全token。所以,在大多数情况下需要调用@Html.AntiForgeryToken()方法。
- 它可以从HTTP报头和表单字段中读取请求token。
ABP添加了下面的特征:
- 自动为所有的AJAX请求报头添加防伪造token。
- 提供abp.security.antiForgery.getToken()函数在javascript中获取token,即使你并不怎么需要它。
启动模板已经集成了CSRF保护。如果你需要手动添加到工程中(可能你的工程在我们添加它之前创建的),跟随下面的向导。
首先,当在Startup类的ConfigureServices中添加MVC时,我们需要给全局过滤器添加AutoValidateAntiforgeryTokenAttribute:
services.AddMvc(options => { options.Filters.Add(new AutoValidateAntiforgeryTokenAttribute()); });
因此,所有的MVC actions(除了GET,HEAD,OPTIONS和TRACE)将会自动校验防伪造token。
需要在我们的Layout视图中添加如下的代码:
@using Abp.Web.Security.AntiForgery
@inject IAbpAntiForgeryManager AbpAntiForgeryManager
@{
AbpAntiForgeryManager.SetCookie(Context);
}
这样,所有使用这个视图的页都会包含他。它在javascript端创建并设置合适的token cookies。如果你有多个布局视图,需要将所有布局视图添加这段代码。
这些就是所有我们需要为ASP.NET Core MVC应用做的工作。所有的AJAX请求会自动处理。对于非ajax表单提交,如果你使用asp-*标签,ASP.NET Core会自动添加防伪造字段。所以,一般不需要使用@Html.AntiForgeryToken()。
对于所有的AJAX请求,防伪造token应该在请求报头中提供,如我们之前所描述的那样。这里,我们将看到它是如何完成的。
abp.jquery.js定义了一个AJAX拦截器,它会为每次请求的报头添加防伪造token。它从abp.security.antiForgery.getToken() javascript函数获得token。
Angular自动为所有的AJAX请求添加防伪造token。参见Angularjs $http文档中的Cross Site Request Forgery(XSRF)保护。ABP默认使用相同的cookies和header名称。所以,Angular集成是开箱即用的。
对于AJAX请求,如果你使用任何其他类库,有以下三种选择:
因为所有的类库都会使用javascript本地的AJAX对象,XMLHttpRequest,你可以定义一个简单的拦截器在报头中添加token:
(function (send) { XMLHttpRequest.prototype.send = function (data) { this.setRequestHeader(abp.security.antiForgery.tokenHeaderName, abp.security.antiForgery.getToken()); return send.call(this, data); }; })(XMLHttpRequest.prototype.send);
好的类库会提供拦截点(如jquery和angularjs)。所以,跟随你供应商的文档来学习如何拦截请求和操作报头。
最后的选择,你可以使用abp.security.antiForgery.getToken()来获取token并手动为每个请求添加报头。但是,你几乎不需要这样做,按如上描述的就可解决问题。
你可能惊讶ABP是如何处理它的?实际上,我们使用了在之前提到的angularjs文档中相同的机制。ABP在cookie中存储token并使用这个cookie设置请求报头。它也很好的集成到了ASP.NET MVC,Web API和Core框架,以便能够校验它。