在 ASP.NET Core 防止跨站点请求伪造 (XSRF/CSRF) 攻击Prevent Cross-Site Request Forgery (XSRF/CSRF) attacks in ASP.NET Core

通过Steve SmithFiyaz Hasan,和Rick AndersonBy Steve Smith, Fiyaz Hasan, and Rick Anderson

跨站点请求伪造 (也称为 XSRF 或 CSRF,读作 ,请参阅冲浪) 是针对恶意网站凭此可以影响客户端浏览器和信任的 web 应用之间的交互的 web 托管的应用程序的攻击浏览器。Cross-site request forgery (also known as XSRF or CSRF, pronounced see-surf) is an attack against web-hosted apps whereby a malicious web app can influence the interaction between a client browser and a web app that trusts that browser. 这些攻击是可能的因为 web 浏览器将自动随每个请求某些类型的身份验证令牌发送到网站。These attacks are possible because web browsers send some types of authentication tokens automatically with every request to a website. 这种形式的攻击是也称为一键式攻击会话行进因为此攻击利用用户的身份验证会话。This form of exploit is also known as a one-click attack or session riding because the attack takes advantage of the user's previously authenticated session.

CSRF 攻击的示例:An example of a CSRF attack:

  1. 用户登录到www.good-banking-site.com使用窗体身份验证。A user signs into www.good-banking-site.com using forms authentication. 服务器对用户进行身份验证,并会发出包含身份验证 cookie 的响应。The server authenticates the user and issues a response that includes an authentication cookie. 站点是易受到攻击,因为它信任的任何请求都收到与有效的身份验证 cookie。The site is vulnerable to attack because it trusts any request that it receives with a valid authentication cookie.

  2. 在用户访问恶意站点, www.bad-crook-site.comThe user visits a malicious site, www.bad-crook-site.com.

    恶意网站www.bad-crook-site.com,包含 HTML 窗体如下所示:The malicious site, www.bad-crook-site.com, contains an HTML form similar to the following:

    <h1>Congratulations! You're a Winner!</h1>
    <form action="http://good-banking-site.com/api/account" method="post">
        <input type="hidden" name="Transaction" value="withdraw">
        <input type="hidden" name="Amount" value="1000000">
        <input type="submit" value="Click to collect your prize!">
    </form>
    

    请注意,窗体的action帖子到易受攻击的站点上,而不是恶意站点。Notice that the form's action posts to the vulnerable site, not to the malicious site. 这是 CSRF 的"跨站点"部分。This is the "cross-site" part of CSRF.

  3. 用户选择提交按钮。The user selects the submit button. 浏览器发出请求,并会自动包括请求的域的身份验证 cookie www.good-banking-site.comThe browser makes the request and automatically includes the authentication cookie for the requested domain, www.good-banking-site.com.

  4. 运行该请求www.good-banking-site.com与用户的身份验证上下文的服务器,并且可以执行身份验证的用户可以执行任何操作。The request runs on the www.good-banking-site.com server with the user's authentication context and can perform any action that an authenticated user is allowed to perform.

除了其中用户选择按钮以提交窗体方案中,恶意网站可以:In addition to the scenario where the user selects the button to submit the form, the malicious site could:

  • 运行自动提交窗体的脚本。Run a script that automatically submits the form.
  • AJAX 请求用以发送窗体提交。Send the form submission as an AJAX request.
  • 隐藏窗体使用 CSS。Hide the form using CSS.

这些替代方案不需要任何操作或从用户而不是最初访问恶意网站的输入。These alternative scenarios don't require any action or input from the user other than initially visiting the malicious site.

使用 HTTPS,则不会阻止 CSRF 攻击。Using HTTPS doesn't prevent a CSRF attack. 可以发送恶意站点 https://www.good-banking-site.com/ 请求一样方便它可以发送不安全的请求。The malicious site can send an https://www.good-banking-site.com/ request just as easily as it can send an insecure request.

一些攻击目标终结点的响应 GET 请求,在这种情况下的图像标记可用于执行的操作。Some attacks target endpoints that respond to GET requests, in which case an image tag can be used to perform the action. 这种形式的攻击的允许的映像,但阻止 JavaScript 论坛站点上很常见。This form of attack is common on forum sites that permit images but block JavaScript. 应用程序来更改的状态的 GET 请求,在其中更改变量或资源,是很容易受到恶意攻击。Apps that change state on GET requests, where variables or resources are altered, are vulnerable to malicious attacks. 更改状态的 GET 请求将不安全。最佳做法是永远不会更改上的 GET 请求的状态。GET requests that change state are insecure. A best practice is to never change state on a GET request.

CSRF 攻击是可能使用 cookie 进行身份验证,因为 web 应用:CSRF attacks are possible against web apps that use cookies for authentication because:

  • 浏览器存储 cookie 颁发的 web 应用。Browsers store cookies issued by a web app.
  • 存储的 cookie 包括用于身份验证的用户的会话 cookie。Stored cookies include session cookies for authenticated users.
  • 浏览器发送的所有 cookie 与域关联到 web 应用而不考虑对应用程序的请求如何生成在浏览器中的每个请求。Browsers send all of the cookies associated with a domain to the web app every request regardless of how the request to app was generated within the browser.

但是,到限于 CSRF 攻击利用 cookie。However, CSRF attacks aren't limited to exploiting cookies. 例如,基本和摘要式身份验证也是易受攻击的。For example, Basic and Digest authentication are also vulnerable. 浏览器使用基本或摘要式身份验证的用户登录后,直到在会话自动发送凭据†结束。After a user signs in with Basic or Digest authentication, the browser automatically sends the credentials until the session† ends.

†在此上下文中,会话指的是在此期间用户进行身份验证的客户端会话。†In this context, session refers to the client-side session during which the user is authenticated. 它是与服务器端会话无关或ASP.NET Core 会话中间件It's unrelated to server-side sessions or ASP.NET Core Session Middleware.

用户可以通过采取预防措施来防止 CSRF 漏洞:Users can guard against CSRF vulnerabilities by taking precautions:

  • 从 web 应用时将它们完成相应操作进行签名。Sign off of web apps when finished using them.
  • 定期清除浏览器 cookie。Clear browser cookies periodically.

但是,CSRF 漏洞从根本上说是 web 应用,而不是最终用户的问题。However, CSRF vulnerabilities are fundamentally a problem with the web app, not the end user.

身份验证基础知识Authentication fundamentals

基于 cookie 的身份验证是常用形式的身份验证。Cookie-based authentication is a popular form of authentication. 基于令牌的身份验证系统越来越流行,尤其是对于单页面应用程序 (Spa)。Token-based authentication systems are growing in popularity, especially for Single Page Applications (SPAs).

当用户通过使用其用户名和密码时,它们被颁发一个令牌,其中包含可用于身份验证和授权的身份验证票证。When a user authenticates using their username and password, they're issued a token, containing an authentication ticket that can be used for authentication and authorization. 作为一个附带的每个请求客户端的 cookie 会使存储令牌。The token is stored as a cookie that accompanies every request the client makes. 由 Cookie 身份验证中间件执行生成和验证此 cookie。Generating and validating this cookie is performed by the Cookie Authentication Middleware. 中间件的加密 cookie 序列化为一个用户主体。The middleware serializes a user principal into an encrypted cookie. 在后续请求中,中间件将验证 cookie、 重新创建主体,并将分配到主体用户的属性HttpContextOn subsequent requests, the middleware validates the cookie, recreates the principal, and assigns the principal to the User property of HttpContext.

基于令牌的身份验证Token-based authentication

当用户进行身份验证时,它们被颁发的令牌 (不防伪令牌)。When a user is authenticated, they're issued a token (not an antiforgery token). 该令牌包含的窗体中的用户信息声明或指向用户状态保留在应用程序的应用程序的引用标记。The token contains user information in the form of claims or a reference token that points the app to user state maintained in the app. 当用户尝试访问资源需要身份验证时,则会将令牌发送到其他 authorization 标头中的持有者令牌的窗体应用。When a user attempts to access a resource requiring authentication, the token is sent to the app with an additional authorization header in form of Bearer token. 这使得应用程序无状态。This makes the app stateless. 在每个后续请求的令牌请求中传递的服务器端验证。In each subsequent request, the token is passed in the request for server-side validation. 此令牌不是加密; 它具有编码This token isn't encrypted; it's encoded. 在服务器上,将解码令牌来访问其信息。On the server, the token is decoded to access its information. 若要在后续请求中发送令牌,请在浏览器的本地存储中存储令牌。To send the token on subsequent requests, store the token in the browser's local storage. 如果该令牌存储在浏览器的本地存储,则不会担心 CSRF 的漏洞。Don't be concerned about CSRF vulnerability if the token is stored in the browser's local storage. CSRF 令牌存储在 cookie 中时是一个问题。CSRF is a concern when the token is stored in a cookie.

在一个域上托管的多个应用Multiple apps hosted at one domain

共享宿主环境为易受到会话劫持、 登录名 CSRF 和其他攻击。Shared hosting environments are vulnerable to session hijacking, login CSRF, and other attacks.

尽管example1.contoso.netexample2.contoso.net是不同的主机,在主机之间没有隐式信任关系*.contoso.net域。Although example1.contoso.net and example2.contoso.net are different hosts, there's an implicit trust relationship between hosts under the *.contoso.net domain. 此隐式信任关系允许可能不受信任的主机会影响彼此的 cookie (控制 AJAX 请求的同域策略不一定适用于 HTTP cookie)。This implicit trust relationship allows potentially untrusted hosts to affect each other's cookies (the same-origin policies that govern AJAX requests don't necessarily apply to HTTP cookies).

通过不共享域,可以防止攻击,可利用同一个域上托管的应用程序之间的受信任的 cookie。Attacks that exploit trusted cookies between apps hosted on the same domain can be prevented by not sharing domains. 在其自己的域中托管每个应用,则时利用任何隐式 cookie 信任关系。When each app is hosted on its own domain, there is no implicit cookie trust relationship to exploit.

ASP.NET Core antiforgery 配置ASP.NET Core antiforgery configuration

警告

ASP.NET Core 实现 antiforgery 使用ASP.NET Core 数据保护ASP.NET Core implements antiforgery using ASP.NET Core Data Protection. 数据保护堆栈必须配置为在服务器场中工作。The data protection stack must be configured to work in a server farm. 请参阅配置数据保护有关详细信息。See Configuring data protection for more information.

在 ASP.NET Core 2.0 或更高版本, FormTagHelper antiforgery 令牌注入 HTML 窗体元素。In ASP.NET Core 2.0 or later, the FormTagHelper injects antiforgery tokens into HTML form elements. Razor 文件中的以下标记自动生成防伪令牌:The following markup in a Razor file automatically generates antiforgery tokens:

<form method="post">
    ...
</form>

同样, IHtmlHelper.BeginForm默认情况下生成防伪令牌,如果窗体的方法不是 GET。Similarly, IHtmlHelper.BeginForm generates antiforgery tokens by default if the form's method isn't GET.

HTML 窗体元素自动生成的防伪令牌发生时<form>标记包含method="post"属性和以下任一条件成立:The automatic generation of antiforgery tokens for HTML form elements happens when the <form> tag contains the method="post" attribute and either of the following are true:

  • 操作属性为空 (action="")。The action attribute is empty (action="").
  • 操作属性不提供 (<form method="post">)。The action attribute isn't supplied (<form method="post">).

可以禁用自动生成的防伪标记 HTML 窗体元素:Automatic generation of antiforgery tokens for HTML form elements can be disabled:

  • 显式禁用防伪令牌,且asp-antiforgery属性:Explicitly disable antiforgery tokens with the asp-antiforgery attribute:

    <form method="post" asp-antiforgery="false">
        ...
    </form>
    
  • 窗体元素是选择向外的标记帮助程序使用标记帮助程序! 选择退出符号:The form element is opted-out of Tag Helpers by using the Tag Helper ! opt-out symbol:

    <!form method="post">
        ...
    </!form>
    
  • 删除FormTagHelper从视图。Remove the FormTagHelper from the view. FormTagHelper可以通过将以下指令添加到 Razor 视图从视图中删除:The FormTagHelper can be removed from a view by adding the following directive to the Razor view:

    @removeTagHelper Microsoft.AspNetCore.Mvc.TagHelpers.FormTagHelper, Microsoft.AspNetCore.Mvc.TagHelpers
    

备注

Razor 页面会自动防范 XSRF/CSRF。Razor Pages are automatically protected from XSRF/CSRF. 有关详细信息,请参阅XSRF/CSRF 和 Razor 页面For more information, see XSRF/CSRF and Razor Pages.

为抵御 CSRF 攻击最常用的方法是使用同步器标记模式(STP)。The most common approach to defending against CSRF attacks is to use the Synchronizer Token Pattern (STP). 当用户请求的页面包含窗体数据使用 STP:STP is used when the user requests a page with form data:

  1. 服务器发送到客户端的当前用户的标识相关联的令牌。The server sends a token associated with the current user's identity to the client.
  2. 客户端返回将令牌发送到服务器进行验证。The client sends back the token to the server for verification.
  3. 如果服务器收到与经过身份验证的用户的标识不匹配的令牌,将拒绝请求。If the server receives a token that doesn't match the authenticated user's identity, the request is rejected.

该令牌唯一且不可预测。The token is unique and unpredictable. 该令牌还可用于确保正确序列化的一系列的请求 (例如,确保请求序列的: 第 1 页–第 2 页–第 3 页)。The token can also be used to ensure proper sequencing of a series of requests (for example, ensuring the request sequence of: page 1 – page 2 – page 3). ASP.NET Core MVC 和 Razor 页模板中的窗体的所有生成 antiforgery 令牌。All of the forms in ASP.NET Core MVC and Razor Pages templates generate antiforgery tokens. 以下两个视图的示例生成防伪令牌:The following pair of view examples generate antiforgery tokens:

<form asp-controller="Manage" asp-action="ChangePassword" method="post">
    ...
</form>

@using (Html.BeginForm("ChangePassword", "Manage"))
{
    ...
}

显式添加到防伪令牌<form>而无需使用标记帮助程序与 HTML 帮助程序元素 @Html.AntiForgeryToken :Explicitly add an antiforgery token to a <form> element without using Tag Helpers with the HTML helper @Html.AntiForgeryToken:

<form action="/" method="post">
    @Html.AntiForgeryToken()
</form>

在每个前面的情况下,ASP.NET Core 添加类似于以下一个隐藏的表单字段:In each of the preceding cases, ASP.NET Core adds a hidden form field similar to the following:

<input name="__RequestVerificationToken" type="hidden" value="CfDJ8NrAkS ... s2-m9Yw">

ASP.NET Core 包括三个筛选器来处理 antiforgery 令牌:ASP.NET Core includes three filters for working with antiforgery tokens:

防伪选项Antiforgery options

自定义防伪选项Startup.ConfigureServices:Customize antiforgery options in Startup.ConfigureServices:

services.AddAntiforgery(options => 
{
    // Set Cookie properties using CookieBuilder properties†.
    options.FormFieldName = "AntiforgeryFieldname";
    options.HeaderName = "X-CSRF-TOKEN-HEADERNAME";
    options.SuppressXFrameOptionsHeader = false;
});

†设置防伪Cookie属性使用的属性CookieBuilder类。†Set the antiforgery Cookie properties using the properties of the CookieBuilder class.

选项Option 描述Description
CookieCookie 确定用于创建防伪 cookie 的设置。Determines the settings used to create the antiforgery cookies.
FormFieldNameFormFieldName 防伪系统用于呈现防伪令牌在视图中的隐藏的窗体字段的名称。The name of the hidden form field used by the antiforgery system to render antiforgery tokens in views.
HeaderNameHeaderName 防伪系统使用的标头的名称。The name of the header used by the antiforgery system. 如果null,系统会认为只有窗体数据。If null, the system considers only form data.
SuppressXFrameOptionsHeaderSuppressXFrameOptionsHeader 指定是否禁止显示生成X-Frame-Options标头。Specifies whether to suppress generation of the X-Frame-Options header. 默认情况下,值为"SAMEORIGIN"生成标头。By default, the header is generated with a value of "SAMEORIGIN". 默认为 falseDefaults to false.
services.AddAntiforgery(options => 
{
    options.CookieDomain = "contoso.com";
    options.CookieName = "X-CSRF-TOKEN-COOKIENAME";
    options.CookiePath = "Path";
    options.FormFieldName = "AntiforgeryFieldname";
    options.HeaderName = "X-CSRF-TOKEN-HEADERNAME";
    options.RequireSsl = false;
    options.SuppressXFrameOptionsHeader = false;
});
选项Option 描述Description
CookieCookie 确定用于创建防伪 cookie 的设置。Determines the settings used to create the antiforgery cookies.
CookieDomainCookieDomain Cookie 的域。The domain of the cookie. 默认为 nullDefaults to null. 此属性已过时,将在未来版本中删除。This property is obsolete and will be removed in a future version. 建议的替代项是 Cookie.Domain。The recommended alternative is Cookie.Domain.
CookieNameCookieName Cookie 的名称。The name of the cookie. 如果未设置,则系统生成唯一的名称开头DefaultCookiePrefix ("。AspNetCore.Antiforgery。") 下。If not set, the system generates a unique name beginning with the DefaultCookiePrefix (".AspNetCore.Antiforgery."). 此属性已过时,将在未来版本中删除。This property is obsolete and will be removed in a future version. 建议的替代项是 Cookie.Name。The recommended alternative is Cookie.Name.
CookiePathCookiePath 设置 cookie 的路径。The path set on the cookie. 此属性已过时,将在未来版本中删除。This property is obsolete and will be removed in a future version. 建议的替代项是 Cookie.Path。The recommended alternative is Cookie.Path.
FormFieldNameFormFieldName 防伪系统用于呈现防伪令牌在视图中的隐藏的窗体字段的名称。The name of the hidden form field used by the antiforgery system to render antiforgery tokens in views.
HeaderNameHeaderName 防伪系统使用的标头的名称。The name of the header used by the antiforgery system. 如果null,系统会认为只有窗体数据。If null, the system considers only form data.
RequireSslRequireSsl 指定是否由防伪系统需要 HTTPS。Specifies whether HTTPS is required by the antiforgery system. 如果true,非 HTTPS 发出的请求失败。If true, non-HTTPS requests fail. 默认为 falseDefaults to false. 此属性已过时,将在未来版本中删除。This property is obsolete and will be removed in a future version. 建议的替代项是设置 Cookie.SecurePolicy。The recommended alternative is to set Cookie.SecurePolicy.
SuppressXFrameOptionsHeaderSuppressXFrameOptionsHeader 指定是否禁止显示生成X-Frame-Options标头。Specifies whether to suppress generation of the X-Frame-Options header. 默认情况下,值为"SAMEORIGIN"生成标头。By default, the header is generated with a value of "SAMEORIGIN". 默认为 falseDefaults to false.

有关详细信息,请参阅CookieAuthenticationOptionsFor more information, see CookieAuthenticationOptions.

配置与 IAntiforgery 防伪功能Configure antiforgery features with IAntiforgery

IAntiforgery提供 API 来配置防伪功能。IAntiforgery provides the API to configure antiforgery features. IAntiforgery 可以在请求Configure方法的Startup类。IAntiforgery can be requested in the Configure method of the Startup class. 以下示例使用中间件应用程序的主页上生成防伪令牌并将其在响应中发送作为 cookie (使用本主题后面所述的默认 Angular 命名约定):The following example uses middleware from the app's home page to generate an antiforgery token and send it in the response as a cookie (using the default Angular naming convention described later in this topic):

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))
        {
            // The request token can be sent as a JavaScript-readable cookie, 
            // and Angular uses it by default.
            var tokens = antiforgery.GetAndStoreTokens(context);
            context.Response.Cookies.Append("XSRF-TOKEN", tokens.RequestToken, 
                new CookieOptions() { HttpOnly = false });
        }

        return next(context);
    });
}

需要防伪验证Require antiforgery validation

ValidateAntiForgeryToken是操作筛选器可应用到单个操作,在控制器或全局范围内。ValidateAntiForgeryToken is an action filter that can be applied to an individual action, a controller, or globally. 除非请求包含有效的防伪令牌,将阻止对已应用此筛选器的操作发出的请求。Requests made to actions that have this filter applied are blocked unless the request includes a valid antiforgery token.

[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 请求令牌。The ValidateAntiForgeryToken attribute requires a token for requests to the action methods it decorates, including HTTP GET requests. 如果ValidateAntiForgeryToken特性应用于应用程序的控制器,它可以替换IgnoreAntiforgeryToken属性。If the ValidateAntiForgeryToken attribute is applied across the app's controllers, it can be overridden with the IgnoreAntiforgeryToken attribute.

备注

ASP.NET Core 不支持自动将 antiforgery 令牌添加到 GET 请求。ASP.NET Core doesn't support adding antiforgery tokens to GET requests automatically.

自动验证防伪令牌仅不安全的 HTTP 方法Automatically validate antiforgery tokens for unsafe HTTP methods only

ASP.NET Core 应用不生成 antiforgery 令牌进行安全的 HTTP 方法 (GET、 HEAD、 选项和跟踪)。ASP.NET Core apps don't generate antiforgery tokens for safe HTTP methods (GET, HEAD, OPTIONS, and TRACE). 而不是广泛应用ValidateAntiForgeryToken属性,然后将其与重写IgnoreAntiforgeryToken属性, AutoValidateAntiforgeryToken属性可用。Instead of broadly applying the ValidateAntiForgeryToken attribute and then overriding it with IgnoreAntiforgeryToken attributes, the AutoValidateAntiforgeryToken attribute can be used. 此属性工作原理相同于ValidateAntiForgeryToken属性,只不过它不需要使用以下 HTTP 方法发出的请求令牌:This attribute works identically to the ValidateAntiForgeryToken attribute, except that it doesn't require tokens for requests made using the following HTTP methods:

  • GETGET
  • HEADHEAD
  • 选项OPTIONS
  • TRACETRACE

我们建议使用AutoValidateAntiforgeryToken广泛用于非 API 方案。We recommend use of AutoValidateAntiforgeryToken broadly for non-API scenarios. 这可确保默认被保护 POST 操作。This ensures POST actions are protected by default. 替代方法是忽略防伪令牌,默认情况下,除非ValidateAntiForgeryToken应用于各个操作方法。The alternative is to ignore antiforgery tokens by default, unless ValidateAntiForgeryToken is applied to individual action methods. 它更有可能在此方案中保留的 POST 操作方法不受保护错误地使应用程序容易受到 CSRF 攻击。It's more likely in this scenario for a POST action method to be left unprotected by mistake, leaving the app vulnerable to CSRF attacks. 所有Post请求都应发送防伪令牌。All POSTs should send the antiforgery token.

Api 没有发送令牌的非 cookie 一部分的自动机制。APIs don't have an automatic mechanism for sending the non-cookie part of the token. 实现可能取决于客户端代码实现。The implementation probably depends on the client code implementation. 一些示例如下所示:Some examples are shown below:

类级别的示例:Class-level example:

[Authorize]
[AutoValidateAntiforgeryToken]
public class ManageController : Controller
{

全局示例:Global example:

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

重写全局或控制器防伪特性Override global or controller antiforgery attributes

IgnoreAntiforgeryToken使用筛选器不再需要给定操作 (或控制器) 的防伪令牌。The IgnoreAntiforgeryToken filter is used to eliminate the need for an antiforgery token for a given action (or controller). 当应用时,此筛选器将重写ValidateAntiForgeryTokenAutoValidateAntiforgeryToken(全局或控制器上) 在较高级别指定的筛选器。When applied, this filter overrides ValidateAntiForgeryToken and AutoValidateAntiforgeryToken filters specified at a higher level (globally or on a controller).

[Authorize]
[AutoValidateAntiforgeryToken]
public class ManageController : Controller
{
    [HttpPost]
    [IgnoreAntiforgeryToken]
    public async Task<IActionResult> DoSomethingSafe(SomeViewModel model)
    {
        // no antiforgery token required
    }
}

刷新令牌的身份验证后Refresh tokens after authentication

用户进行身份验证通过将用户重定向到一个视图或 Razor 页面页后,应刷新令牌。Tokens should be refreshed after the user is authenticated by redirecting the user to a view or Razor Pages page.

JavaScript、 AJAX 和 SpaJavaScript, AJAX, and SPAs

在传统的基于 HTML 的应用中,防伪令牌将传递给使用隐藏的表单域的服务器。In traditional HTML-based apps, antiforgery tokens are passed to the server using hidden form fields. 在基于 JavaScript 的新型应用程序和 Spa 中,以编程方式进行很多请求。In modern JavaScript-based apps and SPAs, many requests are made programmatically. 这些 AJAX 请求可以使用其他技术 (如请求标头或 cookie) 来发送令牌。These AJAX requests may use other techniques (such as request headers or cookies) to send the token.

如果使用 cookie 来存储身份验证令牌,并在服务器上的 API 请求进行身份验证,CSRF 是一个潜在的问题。If cookies are used to store authentication tokens and to authenticate API requests on the server, CSRF is a potential problem. 如果本地存储用于存储令牌,因为从本地存储的值不会自动发送到每个请求的服务器可能会缓解 CSRF 的漏洞。If local storage is used to store the token, CSRF vulnerability might be mitigated because values from local storage aren't sent automatically to the server with every request. 因此,使用本地存储来存储客户端和发送令牌作为请求标头是建议的方法上的防伪令牌。Thus, using local storage to store the antiforgery token on the client and sending the token as a request header is a recommended approach.

JavaScriptJavaScript

使用 JavaScript 与视图,该令牌可以创建使用从视图中的服务。Using JavaScript with views, the token can be created using a service from within the view. 注入Microsoft.AspNetCore.Antiforgery.IAntiforgery到视图并调用服务GetAndStoreTokens:Inject the Microsoft.AspNetCore.Antiforgery.IAntiforgery service into the view and call 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>
</div>

此方法无需直接处理从服务器设置 cookie 或从客户端读取它们。This approach eliminates the need to deal directly with setting cookies from the server or reading them from the client.

前面的示例中使用 JavaScript 为 POST 的 AJAX 标头中读取的隐藏的字段值。The preceding example uses JavaScript to read the hidden field value for the AJAX POST header.

JavaScript 还可以访问 cookie 中的令牌和 cookie 的内容使用令牌的值创建的标头。JavaScript can also access tokens in cookies and use the cookie's contents to create a header with the token's value.

context.Response.Cookies.Append("CSRF-TOKEN", tokens.RequestToken, 
    new Microsoft.AspNetCore.Http.CookieOptions { HttpOnly = false });

假设脚本请求调用的标头中发送令牌X-CSRF-TOKEN,配置要寻找的防伪服务X-CSRF-TOKEN标头:Assuming the script requests to send the token in a header called X-CSRF-TOKEN, configure the antiforgery service to look for the X-CSRF-TOKEN header:

services.AddAntiforgery(options => options.HeaderName = "X-CSRF-TOKEN");

以下示例使用 JavaScript 来发出 AJAX 请求与相应的标头:The following example uses JavaScript to make an AJAX request with the appropriate header:

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 == 200) {
            alert(xhttp.responseText);
        } else {
            alert('There was an error processing the AJAX request.');
        }
    }
};
xhttp.open('POST', '/api/password/changepassword', true);
xhttp.setRequestHeader("Content-type", "application/json");
xhttp.setRequestHeader("X-CSRF-TOKEN", csrfToken);
xhttp.send(JSON.stringify({ "newPassword": "ReallySecurePassword999$$$" }));

AngularJSAngularJS

AngularJS 使用到地址 CSRF 的约定。AngularJS uses a convention to address CSRF. 如果服务器发送包含名称的 cookie XSRF-TOKEN,AngularJS$http服务将 cookie 值添加到标头时它将请求发送到服务器。If the server sends a cookie with the name XSRF-TOKEN, the AngularJS $http service adds the cookie value to a header when it sends a request to the server. 此过程是自动的。This process is automatic. 标头不需要显式设置在客户端。The header doesn't need to be set in the client explicitly. 标头名称是X-XSRF-TOKENThe header name is X-XSRF-TOKEN. 服务器应检测此标头,并验证其内容。The server should detect this header and validate its contents.

ASP.NET Core api 以使用这种规定应用程序启动:For ASP.NET Core API to work with this convention in your application startup:

  • 将应用配置为提供的令牌在 cookie 中名为XSRF-TOKENConfigure your app to provide a token in a cookie called XSRF-TOKEN.
  • 查找名为标头将防伪服务配置X-XSRF-TOKENConfigure the antiforgery service to look for a header named X-XSRF-TOKEN.
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))
        {
            // The request token can be sent as a JavaScript-readable cookie, 
            // and Angular uses it by default.
            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)
{
    // Angular's default header name for sending the XSRF token.
    services.AddAntiforgery(options => options.HeaderName = "X-XSRF-TOKEN");
}

查看或下载示例代码如何下载View or download sample code (how to download)

扩展防伪Extend antiforgery

IAntiForgeryAdditionalDataProvider类型允许开发人员能够通过往返过程中的每个标记的其他数据来扩展反 CSRF 系统的行为。The IAntiForgeryAdditionalDataProvider type allows developers to extend the behavior of the anti-CSRF system by round-tripping additional data in each token. GetAdditionalData每次调用方法生成的字段标记,并返回值嵌入生成的令牌中。The GetAdditionalData method is called each time a field token is generated, and the return value is embedded within the generated token. 实现器可以返回一个时间戳、 nonce 或任何其他值,然后调用ValidateAdditionalData验证令牌时验证此数据。An implementer could return a timestamp, a nonce, or any other value and then call ValidateAdditionalData to validate this data when the token is validated. 客户端的用户名已嵌入在生成的令牌中,因此无需包含此信息。The client's username is already embedded in the generated tokens, so there's no need to include this information. 如果令牌包含补充数据但不是IAntiForgeryAdditionalDataProvider是配置,并不验证补充数据。If a token includes supplemental data but no IAntiForgeryAdditionalDataProvider is configured, the supplemental data isn't validated.

其他资源Additional resources