使用 Windows 身份验证时,必须像保护 cookie 一样保护应用程序终结点免受 CSRF 攻击。 浏览器将身份验证上下文隐式发送到服务器,因此需要保护终结点免受 CSRF 攻击。
跨网站请求伪造(也称为 XSRF 或 CSRF)是一种针对 Web 托管应用的攻击,恶意 Web 应用凭此可以影响客户端浏览器与信任该浏览器的 Web 应用之间的交互。 这些攻击出现的原因可能是 Web 浏览器会随着对网站的每个请求自动发送某些类型的身份验证令牌。 这种形式的攻击也称为一键式攻击或会话控制,因为该攻击利用了用户以前经过身份验证的会话。
以 AJAX 请求形式发送表单提交。
使用 CSS 隐藏表单。
除了最初访问恶意网站外,这些替代方案不需要用户执行任何操作或输入。
使用 HTTPS 无法阻止 CSRF 攻击。 恶意网站可以像发送不安全的请求一样轻松地发送 https://www.good-banking-site.com/
请求。
某些攻击以响应 GET 请求的终结点为目标,在这种情况下,可以使用图像标记来执行操作。 这种形式的攻击在允许图像但阻止 JavaScript 的论坛网站上很常见。 更改 GET 请求状态(更改变量或资源)的应用容易受到恶意攻击。 更改状态的 GET 请求不安全。 最佳做法是永不更改 GET 请求的状态。
CSRF 攻击可能会针对使用 cookie 进行身份验证的 Web 应用,原因如下:
浏览器存储 Web 应用发出的 cookie。
存储的 cookie 包含经过身份验证的用户的会话 cookie。
每次请求时,浏览器都会将与域关联的所有 cookie 发送到 Web 应用,而不管对应用的请求是如何在浏览器中生成的。
但是,CSRF 攻击并不局限于利用 cookie。 例如,基本身份验证和摘要式身份验证也容易受到攻击。 用户使用基本或摘要式身份验证登录后,浏览器会自动发送凭据,直到会话结束。
在此上下文中, 会话 是指对用户进行身份验证的客户端会话。 它与服务器端会话或 ASP.NET Core 会话中间件无关。
用户可以采取预防措施来防范 CSRF 漏洞:
使用完 Web 应用后退出登录。
定期清除浏览器 cookie。
但是从根本上说,CSRF 漏洞是 Web 应用的问题,而不是最终用户的问题。
身份验证基础知识
基于 Cookie 的身份验证是一种常用的身份验证形式。 基于令牌的身份验证系统越来越受欢迎,尤其是对于单页应用程序 (SPA)。
Cookie基于 的身份验证
当用户使用用户名和密码进行身份验证时,他们将获得一个令牌,其中包含一个可用于身份验证和授权的验证票证。 令牌存储为 cookie,随客户端发出的每个请求一起发送。 生成并验证此 cookie 是否由 Cookie 身份验证中间件执行。 中间件将用户主体序列化为加密的 cookie。 在后续请求中,中间件将验证 cookie,重新创建主体并将该主体分配给 HttpContext.User 属性。
基于令牌的身份验证
当用户通过身份验证时,他们将获得一个令牌(不是防伪造令牌)。 令牌包含声明形式的用户信息,或将应用指向应用中维护的用户状态的引用令牌。 当用户尝试访问需要身份验证的资源时,令牌将以持有者令牌的形式通过额外的授权标头发送到应用。 此方法使应用无状态。 在每个后续请求中,令牌将在请求中传递以进行服务器端验证。 此令牌未加密,但已编码。 在服务器上,对令牌进行解码以访问其信息。 若要在后续请求中发送令牌,请将令牌存储在浏览器的本地存储中。 如果令牌存储在浏览器的本地存储中,不必担心 CSRF 漏洞。 当令牌存储在 cookie 中时,CSRF 是一个令人担忧的问题。 有关详细信息,请参阅 GitHub 问题 SPA 代码示例添加两 cookie个 。
多个应用托管在一个域中
共享托管环境容易受到会话劫持、登录 CSRF 和其他攻击。
尽管 example1.contoso.net
和 example2.contoso.net
是不同的主机,但 *.contoso.net
域下的主机之间存在隐式信任关系。 这种隐式信任关系允许可能不受信任的主机影响彼此的 cookie(管理 AJAX 请求的同源策略不一定适用于 HTTP cookie)。
利用同一域上托管的应用之间受信任的 cookie 的攻击可以通过不共享域来防止。 当每个应用托管在自己的域中时,就没有隐式 cookie 信任关系可以利用。
ASP.NET Core 防伪造配置
ASP.NET Core 使用 ASP.NET Core 数据保护实现防伪造。 必须将数据保护堆栈配置为在服务器场中工作。 有关详细信息,请参阅配置数据保护。
在 Startup.ConfigureServices
中调用以下 API 之一时,防伪造中间件将添加到依赖关系注入容器:
AddMvc
MapRazorPages
MapControllerRoute
MapBlazorHub
在 ASP.NET Core 2.0 或更高版本中,FormTagHelper 将防伪造令牌注入 HTML 表单元素。 Razor 文件的以下标记将自动生成防伪造令牌:
<form method="post">
</form>
同样,如果表单的方法不是 GET,则 IHtmlHelper.BeginForm 默认生成防伪造令牌。
当 <form>
标记包含 method="post"
属性且满足以下任一条件时,将针对 HTML 表单元素自动生成防伪造令牌:
action 属性为空 (action=""
)。
未提供 action 属性 (<form method="post">
)。
可以禁止针对 HTML 表单元素自动生成防伪造令牌:
使用 asp-antiforgery
属性显式禁用防伪造令牌:
<form method="post" asp-antiforgery="false">
</form>
表单元素通过使用标记帮助程序 ! 选择退出符号选择退出标记帮助程序:
<!form method="post">
</!form>
从视图中删除 FormTagHelper
。 可通过将以下指令添加到 Razor 视图,从视图中删除 FormTagHelper
:
@removeTagHelper Microsoft.AspNetCore.Mvc.TagHelpers.FormTagHelper, Microsoft.AspNetCore.Mvc.TagHelpers
Razor 页面 会自动受到 XSRF/CSRF 保护。 有关详细信息,请参阅 XSRF/CSRF 和 Razor Pages。
防御 CSRF 攻击的最常见方法是使用同步器令牌模式 (STP)。 当用户请求包含表单数据的页面时,使用 STP:
服务器向客户端发送与当前用户标识相关联的令牌。
客户端将该令牌发送回服务器进行验证。
如果服务器收到的令牌与经过身份验证的用户标识不匹配,则请求会被拒绝。
令牌是唯一且不可预测的。 令牌还可用于确保对一系列请求进行正确的排序(例如,确保请求顺序为:第 1 页 > 第 2 页 > 第 3 页)。 ASP.NET Core MVC 和 Razor Pages 模板中的所有表单都会生成防伪造令牌。 下面这对视图示例生成防伪造令牌:
<form asp-controller="Todo" asp-action="Create" method="post">
</form>
@using (Html.BeginForm("Create", "Todo"))
将防伪造令牌显式添加到 <form>
元素,而无需结合使用标记帮助程序与 HTML 帮助程序 @Html.AntiForgeryToken
:
<form action="/" method="post">
@Html.AntiForgeryToken()
</form>
在上述每个示例中,ASP.NET Core 添加类似于以下示例的隐藏表单域:
<input name="__RequestVerificationToken" type="hidden" value="CfDJ8NrAkS ... s2-m9Yw">
ASP.NET Core 包含三个用于处理防伪造令牌的筛选器:
ValidateAntiForgeryToken
AutoValidateAntiforgeryToken
IgnoreAntiforgeryToken
防伪造选项
在 Startup.ConfigureServices
中自定义 AntiforgeryOptions:
services.AddAntiforgery(options =>
options.FormFieldName = "AntiforgeryFieldname";
options.HeaderName = "X-CSRF-TOKEN-HEADERNAME";
options.SuppressXFrameOptionsHeader = false;
使用 类的属性CookieBuilder设置防伪造Cookie
属性,如下表所示。
有关详细信息,请参阅 CookieAuthenticationOptions 。
IAntiforgery 提供用于配置防伪造功能的 API。 可以在 Startup
类的 Configure
方法中请求 IAntiforgery
。
在以下示例中:
应用主页中的中间件用于生成防伪造令牌,并在响应中将其作为 cookie发送。
请求令牌以 JavaScript 可cookie读的方式发送,并采用Angular部分所述的JS默认Angular命名约定。
public void Configure(IApplicationBuilder app, IAntiforgery antiforgery)
app.Use(next => context =>
string path = context.Request.Path.Value;
if (string.Equals(path, "/", StringComparison.OrdinalIgnoreCase) ||
string.Equals(path, "/index.html", StringComparison.OrdinalIgnoreCase))
var tokens = antiforgery.GetAndStoreTokens(context);
context.Response.Cookies.Append("XSRF-TOKEN", tokens.RequestToken,
new CookieOptions() { HttpOnly = false });
return next(context);
需要防伪造验证
ValidateAntiForgeryToken 是一个操作筛选器,可应用于单个操作、控制器或全局操作。 除非请求包含有效的防伪造令牌,否则对已应用此筛选器的操作的请求将被阻止。
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> RemoveLogin(RemoveLoginViewModel account)
ManageMessageId? message = ManageMessageId.Error;
var user = await GetCurrentUserAsync();
if (user != null)
var result =
await _userManager.RemoveLoginAsync(
user, account.LoginProvider, account.ProviderKey);
if (result.Succeeded)
await _signInManager.SignInAsync(user, isPersistent: false);
message = ManageMessageId.RemoveLoginSuccess;
return RedirectToAction(nameof(ManageLogins), new { Message = message });
ValidateAntiForgeryToken
属性需要令牌才能请求它标记的操作方法,包括 HTTP GET 请求。 如果 ValidateAntiForgeryToken
属性应用于应用的控制器,则可使用 IgnoreAntiforgeryToken
属性替代该属性。
ASP.NET Core 不支持自动将防伪造令牌添加到 GET 请求。
仅自动验证不安全 HTTP 方法的防伪造令牌
ASP.NET Core 应用不会为安全的 HTTP 方法(GET、HEAD、OPTIONS 和 TRACE)生成防伪造令牌。 可以使用 AutoValidateAntiforgeryToken 属性,而不是广泛应用 ValidateAntiForgeryToken
属性,然后使用 IgnoreAntiforgeryToken
属性将其替代。 此属性与 ValidateAntiForgeryToken
属性的工作原理相同,只不过它不需要令牌即可使用以下 HTTP 方法发出请求:
OPTIONS
TRACE
建议将 AutoValidateAntiforgeryToken
广泛用于非 API 方案。 此属性可确保 POST 操作在默认情况下受到保护。 替代方法是默认忽略防伪造令牌,除非 ValidateAntiForgeryToken
应用于单个操作方法。 在这种情况下,更有可能错误地使 POST 操作方法不受保护,从而使应用容易受到 CSRF 攻击。 所有 POST 都应发送防伪造令牌。
API 没有自动机制来发送令牌的非 cookie 部分。 该实现可能取决于客户端代码实现。 下面是一些示例:
类级别示例:
[Authorize]
[AutoValidateAntiforgeryToken]
public class ManageController : Controller
全局示例:
services.AddControllersWithViews(options =>
options.Filters.Add(new AutoValidateAntiforgeryTokenAttribute()));
替代全局或控制器防伪造属性
如果使用 IgnoreAntiforgeryToken 筛选器,给定操作(或控制器)无需防伪造令牌。 应用后,此筛选器将替代更高级别(全局或控制器上)指定的 ValidateAntiForgeryToken
和 AutoValidateAntiforgeryToken
筛选器。
[Authorize]
[AutoValidateAntiforgeryToken]
public class ManageController : Controller
[HttpPost]
[IgnoreAntiforgeryToken]
public async Task<IActionResult> DoSomethingSafe(SomeViewModel model)
// no antiforgery token required
身份验证后刷新令牌
将用户重定向到某个视图或 Razor Pages 页面进行身份验证后,应刷新令牌。
JavaScript、AJAX 和 SPA
在基于 HTML 的传统应用中,防伪造令牌使用隐藏表单域传递给服务器。 在基于 JavaScript 的新式应用和 SPA 中,许多请求都是以编程方式进行。 这些 AJAX 请求可以使用其他技术(例如请求头或 cookie)发送令牌。
如果使用 cookie 来存储身份验证令牌并在服务器上对 API 请求进行身份验证,则 CSRF 是一个潜在问题。 如果使用本地存储来存储令牌,CSRF 漏洞问题可能会得到缓解,因为本地存储中的值不会随每个请求自动发送到服务器。 建议使用本地存储在客户端上存储防伪造令牌,并将令牌作为请求头发送。
JavaScript
结合使用 JavaScript 与视图,可以在视图中使用服务创建令牌。 将 IAntiforgery 服务注入视图并调用 GetAndStoreTokens:
ViewData["Title"] = "AJAX Demo";
@inject Microsoft.AspNetCore.Antiforgery.IAntiforgery Xsrf
@functions{
public string GetAntiXsrfRequestToken()
return Xsrf.GetAndStoreTokens(Context).RequestToken;
<input type="hidden" id="RequestVerificationToken"
name="RequestVerificationToken" value="@GetAntiXsrfRequestToken()">
<h2>@ViewData["Title"].</h2>
<h3>@ViewData["Message"]</h3>
<div class="row">
<p><input type="button" id="antiforgery" value="Antiforgery"></p>
<script>
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
if (xhttp.readyState == XMLHttpRequest.DONE) {
if (xhttp.status == 200) {
alert(xhttp.responseText);
} else {
alert('There was an error processing the AJAX request.');
document.addEventListener('DOMContentLoaded', function() {
document.getElementById("antiforgery").onclick = function () {
xhttp.open('POST', '@Url.Action("Antiforgery", "Home")', true);
xhttp.setRequestHeader("RequestVerificationToken",
document.getElementById('RequestVerificationToken').value);
xhttp.send();
</script>
此方法无需直接从服务器设置 cookie,也无需从客户端读取 cookie。
上述示例使用 JavaScript 读取 AJAX POST 标头的隐藏域值。
JavaScript 还可以访问 cookie 中的令牌,并使用 cookie 的内容创建具有令牌值的标头。
context.Response.Cookies.Append("CSRF-TOKEN", tokens.RequestToken,
new Microsoft.AspNetCore.Http.CookieOptions { HttpOnly = false });
假设脚本请求在名为 X-CSRF-TOKEN
的标头中发送令牌,请将防伪造服务配置为查找 X-CSRF-TOKEN
标头:
services.AddAntiforgery(options => options.HeaderName = "X-CSRF-TOKEN");
以下示例使用 JavaScript 发出带有相应标头的 AJAX 请求:
function getCookie(cname) {
var name = cname + "=";
var decodedCookie = decodeURIComponent(document.cookie);
var ca = decodedCookie.split(';');
for (var i = 0; i < ca.length; i++) {
var c = ca[i];
while (c.charAt(0) === ' ') {
c = c.substring(1);
if (c.indexOf(name) === 0) {
return c.substring(name.length, c.length);
return "";
var csrfToken = getCookie("CSRF-TOKEN");
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function () {
if (xhttp.readyState === XMLHttpRequest.DONE) {
if (xhttp.status === 204) {
alert('Todo item is created successfully.');
} else {
alert('There was an error processing the AJAX request.');
xhttp.open('POST', '/api/items', true);
xhttp.setRequestHeader("Content-type", "application/json");
xhttp.setRequestHeader("X-CSRF-TOKEN", csrfToken);
xhttp.send(JSON.stringify({ "name": "Learn C#" }));
AngularJS
JS Angular使用约定来处理 CSRF。 如果服务器发送cookie名为 XSRF-TOKEN
的 ,则AngularJS$http
服务在向服务器发送请求时将该值添加到cookie标头中。 此过程是自动的。 客户端不需要显式设置 标头。 标头名称为 X-XSRF-TOKEN
。 服务器应检测此标头并验证其内容。
要使 ASP.NET Core API 在应用程序启动时使用此约定:
将应用配置为在名为 XSRF-TOKEN
的 cookie 中提供令牌。
配置防伪造服务以查找名为 的X-XSRF-TOKEN
标头,该标头是Angular用于发送 XSRF 令牌的默认标头名称。
public void Configure(IApplicationBuilder app, IAntiforgery antiforgery)
app.Use(next => context =>
string path = context.Request.Path.Value;
string.Equals(path, "/", StringComparison.OrdinalIgnoreCase) ||
string.Equals(path, "/index.html", StringComparison.OrdinalIgnoreCase))
var tokens = antiforgery.GetAndStoreTokens(context);
context.Response.Cookies.Append("XSRF-TOKEN", tokens.RequestToken,
new CookieOptions() { HttpOnly = false });
return next(context);
public void ConfigureServices(IServiceCollection services)
services.AddAntiforgery(options => options.HeaderName = "X-XSRF-TOKEN");
Windows 身份验证和防伪造cookie
使用 Windows 身份验证时,必须像保护 cookie 一样保护应用程序终结点免受 CSRF 攻击。 浏览器将身份验证上下文隐式发送到服务器,因此需要保护终结点免受 CSRF 攻击。
扩展防伪造
IAntiforgeryAdditionalDataProvider 类型允许开发人员通过往返传递每个令牌中的附加数据来扩展反 CSRF 系统的行为。 每次生成域令牌时,将调用 GetAdditionalData 方法,并且返回值嵌入到生成的令牌中。 实施者可以返回时间戳、nonce 或任何其他值,然后在验证令牌时调用 ValidateAdditionalData 来验证此数据。 客户端的用户名已嵌入到生成的令牌中,因此无需包含此信息。 如果令牌包含补充数据,但没有配置 IAntiForgeryAdditionalDataProvider
,则不验证补充数据。
在 Web 场中托管 ASP.NET Core