项目

CSRF/XSRF与防伪系统

"跨站请求伪造(CSRF)是一种攻击方式:当恶意网站、电子邮件、博客、即时消息或程序诱导用户的网络浏览器在当前已认证的可信站点上执行非预期操作时发生"(引自OWASP)。

ABP框架实现了CSRF防护的全自动化,开箱即用无需任何配置。本文档仅供需要深入理解机制或进行自定义时参考。

问题背景

ASP.NET Core提供了一套基础设施,通过生成和验证防伪令牌来防御CSRF攻击。但标准实现存在以下局限:

防伪令牌验证默认仅针对Razor页面启用,而未对HTTP API启用。开发者需手动为控制器启用验证,可通过为特定API控制器/操作添加[ValidateAntiForgeryToken]特性,或使用[AutoValidateAntiforgeryToken]特性实现全局防护。

启用后存在以下问题:

  • 需要为每个AJAX请求手动添加名为RequestVerificationToken的HTTP头。开发者需关注令牌获取、客户端存储及每次请求的头部添加
  • 所有客户端(包括非浏览器客户端)都需处理令牌获取与发送。实际上非浏览器客户端不存在CSRF风险,不应承担此负担

尤其第二点会为客户端带来额外负担,并无谓消耗服务器资源。

开发者仍可使用ASP.NET Core所有防伪功能(如ValidateAntiForgeryTokenAutoValidateAntiforgeryTokenIgnoreAntiforgeryToken),详见官方文档

解决方案

ABP提供与上述功能对应的[AbpValidateAntiForgeryToken][AbpAutoValidateAntiforgeryToken]特性。其中[AbpAutoValidateAntiforgeryToken]已加入全局过滤器,无需额外配置即可启用。

ABP还自动化实现以下基础设施:

  • 服务端自动设置特殊Cookie(默认名称为XSRF-TOKEN)用于向浏览器提供防伪令牌值。该过程通过应用配置端点自动完成,客户端无需任何操作
  • 客户端从Cookie读取令牌并自动添加到HTTP头部(默认名称为RequestVerificationToken)。该功能在所有支持的UI类型中均已实现
  • 服务端仅验证浏览器发起的同源与跨站请求,对非浏览器客户端跳过验证

整个系统实现了无缝运作。

配置与自定义

AbpAntiForgeryOptions

AbpAntiForgeryOptions是配置ABP防伪系统的核心选项类,包含以下属性:

  • TokenCookie:配置Cookie详细信息。该Cookie用于客户端存储防伪令牌值,默认名称XSRF-TOKEN,过期时间为10年(需长于认证Cookie最长生命周期以确保安全)
  • AuthCookieSchemaName:应用使用的认证Cookie名称。默认值Identity.Application(运行时变为AspNetCore.Identity.Application)。该默认值与ABP启动模板兼容。若修改认证Cookie名称,必须同步修改此值
  • AutoValidate:控制ABP自动防伪验证系统的开关。默认值true
  • AutoValidateFilter:类型判定函数(输入类型返回布尔值)。ABP通过该谓词检查控制器类型,返回false时排除该控制器的自动防伪验证
  • AutoValidateIgnoredHttpMethods:自动防伪验证忽略的HTTP方法列表。默认值:"GET", "HEAD", "TRACE", "OPTIONS"。这些方法不会改变应用状态,可安全跳过验证

如需修改这些选项,请在模块的ConfigureServices方法中配置

示例:配置AbpAntiForgeryOptions

Configure<AbpAntiForgeryOptions>(options =>
{
    options.TokenCookie.Expiration = TimeSpan.FromDays(365);
    options.AutoValidateIgnoredHttpMethods.Remove("GET");
    options.AutoValidateFilter =
        type => !type.Namespace.StartsWith("MyProject.MyIgnoredNamespace");
});

此配置:

  • 设置防伪令牌过期时间为1年
  • 为GET请求启用防伪验证
  • 忽略指定命名空间下的控制器类型

AntiforgeryOptions

AntiforgeryOptions是ASP.NET Core的标准选项类完整文档参见此处

从ABP视角看,HeaderName选项尤为重要。该值默认为RequestVerificationToken,客户端使用该名称发送令牌头部。若修改此选项,需同步调整客户端实现。如无特殊需求,建议保持默认值。

AbpValidateAntiForgeryToken特性

若禁用自动验证或需为默认不验证的端点(如HTTP GET方法)启用验证,可为控制器类型或方法添加[AbpValidateAntiForgeryToken]特性。

示例:为HTTP GET方法添加验证

using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Volo.Abp.AspNetCore.Mvc;
using Volo.Abp.AspNetCore.Mvc.AntiForgery;

namespace MyCompanyName.MyProjectName.Controllers
{
    [Route("api/products")]
    public class ProductController : AbpController
    {
        [HttpGet]
        [AbpValidateAntiForgeryToken]
        public async Task GetAsync()
        {
            //TODO: ...
        }
    }
}

Angular UI集成

Angular原生支持CSRF令牌,但令牌头名称为X-XSRF-TOKEN。由于ABP遵循ASP.NET Core约定,在核心包中已将名称改为RequestVerificationToken

除非需要修改AntiforgeryOptions.HeaderName,否则无需任何操作。若修改头名称,需同步调整Angular应用配置:在根模块中导入HttpClientXsrfModule

示例:将头名称改为MyCustomHeaderName

@NgModule({
  // ...
  imports: [
    //...
    HttpClientXsrfModule.withOptions({
      cookieName: 'XSRF-TOKEN',
      headerName: 'MyCustomHeaderName'
    })
  ],
})
export class AppModule {}

注意: XSRF-TOKEN仅在前端应用与API处于同域时有效。因此发起请求时应使用相对路径。

示例场景:

  • API托管于https://testdomain.com/ws
  • Angular应用托管于https://testdomain.com/admin
  • API请求地址应为:https://testdomain.com/ws/api/identity/users

此时environment.prod.ts配置应为:

export const environment = {
  production: true,
  // ....
  apis: {
    default: {
      url: '/ws', // <- 此处只需填写上下文根路径
     // ...
    },
  },
} as Config.Environment;

原因说明: Angular源代码会跳过所有以http://https://开头的请求——这是合理的:跨站请求无需令牌验证,该机制仅对同域请求有效。因此当所有资源服务于同一域时,只需使用相对路径。

若API直接从根域名提供服务(无上下文根,如https://testdomain.com/api/identity/users),配置应为:

export const environment = {
  production: true,
  // ....
  apis: {
    default: {
      url: '', // <- 应为空字符串而非'/'
     // ...
    },
  },
} as Config.Environment;
在本文档中