如何在ASP.NET Core中默认防范CSRF攻击?能否实现ASP.NET MVC 4同款效果?
如果你之前用过ASP.NET MVC 4,肯定对手动加[ValidateAntiForgeryToken]和Html.AntiForgeryToken()印象深刻吧?现在转到ASP.NET Core,其实它的CSRF(官方更常称XSRF)防护不仅默认更贴心,还能轻松实现和MVC4一样的效果,甚至安全性更高。
ASP.NET Core默认的CSRF防护是怎么玩的?
1. 自动帮你注入验证Token
以前在MVC4里,你得手动在表单里写@Html.AntiForgeryToken()才能生成隐藏字段,但在ASP.NET Core里,只要你用Razor的<form> Tag Helper(或者传统的Html.BeginForm),框架会自动在表单里插入__RequestVerificationToken隐藏字段,省了不少手动操作的功夫。
要是你用AJAX发POST/PUT/DELETE请求,也不用担心,你可以从表单的隐藏字段或者Cookie里拿到Token,然后在请求头里带上RequestVerificationToken,后端会自动验证这个Token。
2. 全局自动验证,不用逐个Action加标记
MVC4里你得给每个需要防护的POST Action手动加[ValidateAntiForgeryToken],或者自己写全局过滤器。ASP.NET Core直接给你提供了[AutoValidateAntiforgeryToken],把它注册成全局过滤器,所有非GET/HEAD/OPTIONS/TRACE的请求都会自动验证CSRF Token,简直省事儿:
// .NET 6+的Program.cs里这么配置 builder.Services.AddControllersWithViews(options => { options.Filters.Add(new AutoValidateAntiforgeryTokenAttribute()); });
当然,如果你想个别Action跳过验证,加个[IgnoreAntiforgeryToken]就行。
3. 双Token机制,安全性拉满
ASP.NET Core默认会生成两个Token:一个存在Cookie里(带HttpOnly、Secure、SameSite属性,防XSS和跨站泄露),另一个存在表单或请求头里。验证的时候会同时对比这两个Token,这种机制比MVC4的单一Token更安全,能防范更多攻击场景。
能不能实现和ASP.NET MVC 4一模一样的效果?
完全可以!甚至说,ASP.NET Core是向下兼容这种“手动模式”的:
- 如果你不想用全局自动验证,那就别加那个全局过滤器,然后给需要防护的Action手动加上
[ValidateAntiForgeryToken],和MVC4一模一样。 - 视图里你也可以手动写
@Html.AntiForgeryToken(),虽然框架已经自动加了,但这么写也没问题,效果和MVC4完全一致。
举个和MVC4对齐的例子:
// ASP.NET Core的Action,和MVC4写法一致 [HttpPost] [ValidateAntiForgeryToken] public IActionResult SubmitForm(FormModel model) { // 处理表单逻辑 return RedirectToAction("Index"); }
<!-- ASP.NET Core视图,手动加Token(可选,因为默认会自动加) --> @using (Html.BeginForm()) { @Html.AntiForgeryToken() <!-- 表单输入字段 --> <input type="text" asp-for="Username" /> <button type="submit">提交</button> }
而且,如果你想让ASP.NET Core的防护级别和MVC4一样(其实不推荐,因为Core更安全),只需要调整一下antiforgery的配置,比如关闭SameSite属性之类的,但真的没必要——毕竟Core的默认配置已经比MVC4安全多了。
额外的安全buff(MVC4没有的)
ASP.NET Core还自带一些MVC4没有的增强特性:
- SameSite Cookie默认配置:默认
SameSite=Lax,能有效防范大多数跨站请求攻击,MVC4得手动折腾配置才能支持。 - Token自动刷新:框架会定期更新Token,降低Token泄露带来的风险。
- SPA/AJAX友好:可以通过
IAntiforgery服务获取Token,方便在单页应用或者AJAX请求里携带,适配现代Web开发场景。
内容的提问来源于stack exchange,提问作者Ludisposed




