6.9 CSRF和XSRF保护

6.9.1 简介

Cross-Site Request Forgery (CSRF) 跨站请求伪造是一种攻击,发生在具有恶意的网站,email,blog,即时消息,或者程序导致用户的web浏览器在一个受信用的网站去执行了某个有害的行为来获取当前用户的认证信息,详细了解请点击这里脑补。

ASP.NET Web API官网也有如何实现的简短描述。

ABP框架尽可能的简化且自动化CSRF保护。启动模板 实现了开箱即用的预配置。在这篇文档中,我们会展示它是如何集成到ASP.NET,以及它是如何工作的。

Http 谓词

对于 HTTP谓词是 GET,HEAD,OPTIONS以及TRACE 的Action,我们不需要进行CSRF保护,因为它们是无副作用的操作(不会更改数据库)。ABP假定(ABP仅对这些谓词实现了反伪造保护,如:POST,PUT,PATH以及DELETE )你能够使用一些已定义的特性改变这些行为(应该是说将 [POST] 等特性加装在以Get等开头的方法上)。

6.9.2 ASP.NET MVC

Features

正如你所了解到的ASP.NET MVC有自己内置的AntiForgery功能,但是它具有以下不完美的地方:

  • 需要对所有的Actions添加 ValidateAntiForgeryToken 才能实现保护。我们可能会忘记添加它到需要执行保护的Action上。

  • ValidateAntiForgeryToken 特性仅检查HTML表单中的 __RequestVerificationToken 字段。这在 AJAX 请求中使用它是很难且不可能的,特别是在你发送的content-type是 application/json 的时候。在AJAX请求中,通常的做法是在 request header 中添加token。

  • 在AJAX请求中我们需要取得token并且使用它,但是在脚本代码中很难取得verification token(特别是你没有在.cshtml文件中写脚本)。

  • 即使我们能够在脚本中得到token,我们应该手动的添加token到每个请求头中。

ABP做了以下一些事情克服了这些困难:

  • 不在需要对这些Action 如:POST,PUT,PATH以及DELETE 添加 ValidateAntiForgeryToken 特性,因为它们会自动的保护起来(通过 AbpAntiForgeryMvcFilter 过滤器)。对于大多数情况自动保护已足够好。但是你也可以使用 DisableAbpAntiForgeryTokenValidation 特性来禁用对某个Action或者Controller的保护,你也可以使用特性 ValidateAbpAntiForgeryToken 对任何你需要的Action/Controller来开启保护。

  • 在脚本中提供了 abp.security.antiForgery.getToken() 函数来取得token,即使你不需要它。

  • 对所有的AJAX请求自动的添加 anti forgery token 到请求头中。

因此,相对于ASP.NET MVC 自带AntiForgery功能,它的几乎是无缝的工作。

集成

开始模板已经对CSRF进行了开箱即用的集成。如果你需要手动添加它到你的项目(你的项目先于我添加该项功能的时候就已经创建),请跟随下面指南:

Layout View

我们应该添加下面代码到我们的布局视图:

@{
    SetAntiForgeryCookie();
}

因此,所有使用了该视图的页面都会包含它。这个方法被定义在ABP视图的基类中。它会创建且设置token到cookie中,并且能在客户端脚本运行。如果你有多个布局视图,请把该代码添加到所有的布局视图中。

这就是我们应该对ASP.NET应用所要做的。所有的AJAX请求会自动的工作。但是我们应该在那些不需要通过AJAX来提交POSt请求的表单中使用 @Html.AntiForgeryToken() 方法(并且对相信的Action我们不需要添加 ValidateAbpAntiForgeryToken 特性)。

Configuration

XSRF保护默认是开启的。你可以在模块的 Preinitialize 方法中禁用。如:

Configuration.Modules.AbpWeb().AntiForgery.IsEnabled = false;

你也可以使用 Configuration.Modules.AbpWebCommon().AntiForgery 来配置token和cookie的名称。

注意:如果使用了伪造验证,如果使用Swagger Ui来调试动态WebApi,请参考 Swagger UI集成的文档。

6.9.3 ASP.NET Web API

Features

ASP.NET Web API 没有提供防伪造这样的机制。当时ABP对ASP.NET Web API提供了CSRF保护的基础设施并且自动完成这个功能。

集成

与 ASP.NET MVC Clients 集成

如果你在MVC项目中使用Web API,不需要任何额外的配置。即使你的Web API层是self-hosting在另外的进程中。不需要任何配置,只需配置好来自MVC应用的AJAX请求。

与其他Clients集成

如果你的客户端是不同种类的应用(没有如前面所述:在一个独立的angularjs中使用 SetAntiForgeryCookie() 方法)。那么你应该提供一个设置反伪造token的cookie。一个可能的方式是像下面一样创建一个Api Controller:

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。

6.9.3 ASP.NET Core

Features

与之前的版本(ASP.NET MVC 5.x)相比,ASP.NET Core MVC 有一个很好的反伪造机制:

  • 它有个 AutoValidateAntiforgeryTokenAttribute 类会自动的对所有的 POST,PUT,PATH以及DELETE Action自动实施反伪造验证。

  • 它有 ValidateAntiForgeryToken和IgnoreAntiforgeryToken 特性来控制toekn验证。

  • 如果你没有禁用反伪造功能,它会自动的添加反伪造安全token在html表单中。所以在大多数情况下你不需要调用 @Html.AntiForgeryToken()

  • 他可以从HTTP头或者Form表单的字段中读取请求的token。

ABP 添加了以下功能:

  • 自动的为所有的AJXA请求添加了反伪造token。

  • 在脚本中提供了 abp.security.antiForgery.getToken() 来取得token,即使你不使用它。

集成

开始模板已经对CSRF进行了开箱即用的集成。如果你需要手动添加它到你的项目(你的项目先于我添加该项功能的时候就已经创建),请跟随下面指南:

Startup Class

首先,当我们在MVC启动类中配置ConfigureServices时,我们应该添加 AutoValidateAntiforgeryTokenAttribute 特性到全局过滤器中。

services.AddMvc(options =>
{
    options.Filters.Add(new AutoValidateAntiforgeryTokenAttribute());
});

因此,所有的MVC Actions将会对反伪造token进行自动验证(除了之前所描述的 GET,HEAD,OPTIONS以及TRACE 外)。

Layout View

我们应该在布局视图中添加下面的代码:

@using Abp.Web.Security.AntiForgery
@inject IAbpAntiForgeryManager AbpAntiForgeryManager
@{
    AbpAntiForgeryManager.SetCookie(Context);
}

因此,所有使用了该视图的页面都会包含它。这个方法被定义在ABP视图的基类中。它会创建且设置token到cookie中,并且能在客户端脚本运行。如果你有多个布局视图,请把该代码添加到所有的布局视图中。

这就是我们应该为ASP.NET Core MVC应用所要做的。所有的AJAX请求会自动的工作。对于非AJAX表单提交,如果你使用在你的表单中使用了asp-* 的标签,ASP.NET Core会自动添加反伪造token字段。所以正常情况下你不需要调用 @Html.AntiForgeryToken()

6.9.4 客户端库

在所有的AJAX请求头中反伪造token应该被提供,正如我们前面所描述的。在这里我们将会看到具体实现。

JQuery

abp.jquery.js 定义了AJAX拦截器,这可以对每个AJAX请求添加反伪造token到请求头中。使用 abp.security.antiForgery.getToken() 方法来取得token。

AngularJs

AngularJs会自动的添加反伪造token到所有的AJAX请求中,详细请查阅Angualrjs的跨站请求伪造保护。ABP默认使用相同的cookie和头名称。所以AngularJs集成了开箱即用的功能。

其他库

如果你使用了其他的脚本库来实现AJAX请求,你有三个选择可以做:

Intercept XMLHttpRequest

因为所有的库都会使用脚本的原生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);
Use Library Interceptor

一个好使用的库会提供拦截点给你如JQuery和angularjs。所以,你应该学习这些库的文档来了解如何拦截请求并操作请求头。

手动添加请求头

最后一种选择,你可以使用 abp.security.antiForgery.getToken() 方法取得token并且手动的添加它到每个AJAX请求中。但是你可能不需要这要做,你可以按照上面所述来解决问题。

Internals

你可能想知道ABP是如何处理它的。事实上,在前面提到的angularjs文档中,我们使用了相同的机制来描述。ABP存储token到cookie(如前所述),并且使用该cookie来设置请求头。为了验证防伪造token,它也很好的集成到了ASP.NET,Web API和Core框架。