防止 ASP.NET Core 中的跨網站要求偽造(XSRF/CSRF)攻擊Prevent Cross-Site Request Forgery (XSRF/CSRF) attacks in ASP.NET Core

作者: Rick AndersonFiyaz HasanSteve SmithBy Rick Anderson, Fiyaz Hasan, and Steve Smith

跨網站偽造要求(也稱為 XSRF 或 CSRF)是對 web 裝載應用程式的攻擊,因此惡意的 web 應用程式可能會影響用戶端瀏覽器與信任該瀏覽器之 web 應用程式之間的互動。Cross-site request forgery (also known as XSRF or CSRF) 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. 這些攻擊是可行的,因為網頁瀏覽器會在每次要求網站時,自動傳送一些類型的驗證權杖。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.comA 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.

針對使用 cookie 進行驗證的 web 應用程式,可能會受到 CSRF 攻擊,因為:CSRF attacks are possible against web apps that use cookies for authentication because:

  • 瀏覽器會儲存 web 應用程式所發出的 cookie。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、重新建立主體,並將主體指派給HttpCoNtextUser屬性。On subsequent requests, the middleware validates the cookie, recreates the principal, and assigns the principal to the User property of HttpContext.

以權杖為基礎的驗證Token-based authentication

當使用者通過驗證時,就會發出權杖(不是 antiforgery token)。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. 當使用者嘗試存取需要驗證的資源時,權杖會以持有人權杖的形式,以額外的授權標頭傳送至應用程式。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. 當令牌儲存在 cookie 中時,CSRF 是一項考慮。CSRF is a concern when the token is stored in a cookie. 如需詳細資訊,請參閱 GitHub 問題SPA 程式碼範例會新增兩個 cookieFor more information, see the GitHub issue SPA code sample adds two cookies.

裝載于一個網域的多個應用程式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 使用 ASP.NET Core 的資料保護來執行 antiforgery。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.

Startup.ConfigureServices中呼叫下列其中一個 Api 時,會將 Antiforgery 中介軟體新增至相依性插入容器:Antiforgery middleware is added to the Dependency injection container when one of the following APIs is called in Startup.ConfigureServices:

在中呼叫 AddMvc 時,Antiforgery 中介軟體會新增至相依性插入容器 Startup.ConfigureServicesAntiforgery middleware is added to the Dependency injection container when AddMvc is called in Startup.ConfigureServices

在 ASP.NET Core 2.0 或更新版本中, FormTagHelper會將 antiforgery TOKEN 插入 HTML 表單元素中。In ASP.NET Core 2.0 or later, the FormTagHelper injects antiforgery tokens into HTML form elements. Razor 檔案中的下列標記會自動產生 antiforgery 權杖:The following markup in a Razor file automatically generates antiforgery tokens:

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

同樣地,如果無法取得表單的方法,則IHtmlHelper會依預設產生 antiforgery token。Similarly, IHtmlHelper.BeginForm generates antiforgery tokens by default if the form's method isn't GET.

<form> 標籤包含 method="post" 屬性,而且下列其中一項為 true 時,就會自動產生 HTML 表單元素的 antiforgery token: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 屬性是空的(action="")。The action attribute is empty (action="").
  • 未提供動作屬性(<form method="post">)。The action attribute isn't supplied (<form method="post">).

可以停用 HTML 表單元素的自動產生 antiforgery token:Automatic generation of antiforgery tokens for HTML form elements can be disabled:

  • 明確停用具有 asp-antiforgery 屬性的 antiforgery token:Explicitly disable antiforgery tokens with the asp-antiforgery attribute:

    <form method="post" asp-antiforgery="false">
        ...
    </form>
    
  • Form 元素會使用標籤協助程式!退出符號來退出宣告標記協助程式:The form element is opted-out of Tag Helpers by using the Tag Helper ! opt-out symbol:

    <!form method="post">
        ...
    </!form>
    
  • 從視圖中移除 FormTagHelperRemove the FormTagHelper from the view. 將下列指示詞新增至 Razor 視圖,即可從視圖中移除 FormTagHelperThe 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 Pages會自動受到 XSRF/CSRF 的保護。Razor Pages are automatically protected from XSRF/CSRF. 如需詳細資訊,請參閱XSRF/CSRF 和 Razor PagesFor 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.

Token 是唯一且無法預測的。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 Pages 範本中的所有表單都會產生 antiforgery token。All of the forms in ASP.NET Core MVC and Razor Pages templates generate antiforgery tokens. 下列對視圖範例會產生 antiforgery token:The following pair of view examples generate antiforgery tokens:

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

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

將 antiforgery token 明確新增至 <form> 專案,而不使用標記協助程式搭配 HTML helper @Html.AntiForgeryTokenExplicitly 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 token 的篩選條件:ASP.NET Core includes three filters for working with antiforgery tokens:

Antiforgery 選項Antiforgery options

Startup.ConfigureServices中自訂antiforgery 選項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;
});

†使用CookieBuilder類別的屬性來設定 antiforgery Cookie 屬性。†Set the antiforgery Cookie properties using the properties of the CookieBuilder class.

選項Option 描述Description
CookieCookie 決定用來建立 antiforgery cookie 的設定。Determines the settings used to create the antiforgery cookies.
FormFieldNameFormFieldName Antiforgery 系統用來轉譯 views 中 antiforgery 標記的隱藏表單欄位名稱。The name of the hidden form field used by the antiforgery system to render antiforgery tokens in views.
HeaderNameHeaderName Antiforgery 系統使用的標頭名稱。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 決定用來建立 antiforgery 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. 網域]。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. 路徑]。The recommended alternative is Cookie.Path.
FormFieldNameFormFieldName Antiforgery 系統用來轉譯 views 中 antiforgery 標記的隱藏表單欄位名稱。The name of the hidden form field used by the antiforgery system to render antiforgery tokens in views.
HeaderNameHeaderName Antiforgery 系統使用的標頭名稱。The name of the header used by the antiforgery system. 如果 null,系統只會考慮表單資料。If null, the system considers only form data.
RequireSslRequireSsl 指定 antiforgery 系統是否需要 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.

如需詳細資訊,請參閱cookieauthenticationoptions.authenticationtypeFor more information, see CookieAuthenticationOptions.

使用 IAntiforgery 設定 antiforgery 功能Configure antiforgery features with IAntiforgery

IAntiforgery提供 API 來設定 antiforgery 功能。IAntiforgery provides the API to configure antiforgery features. IAntiforgery 可以在 Startup 類別的 Configure 方法中要求。IAntiforgery can be requested in the Configure method of the Startup class. 下列範例會使用來自應用程式首頁的中介軟體來產生 antiforgery token,並在回應中將它當做 cookie 傳送(使用本主題稍後所述的預設角度命名慣例):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);
    });
}

需要 antiforgery 驗證Require antiforgery validation

ValidateAntiForgeryToken是可套用至個別動作、控制器或全域的動作篩選準則。ValidateAntiForgeryToken is an action filter that can be applied to an individual action, a controller, or globally. 除非要求包含有效的 antiforgery token,否則會封鎖已套用此篩選之動作的要求。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 marks, 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 token 來取得要求。ASP.NET Core doesn't support adding antiforgery tokens to GET requests automatically.

僅針對 unsafe HTTP 方法自動驗證 antiforgery 權杖Automatically validate antiforgery tokens for unsafe HTTP methods only

ASP.NET Core 應用程式不會為安全的 HTTP 方法(GET、HEAD、OPTIONS 和 TRACE)產生 antiforgery token。ASP.NET Core apps don't generate antiforgery tokens for safe HTTP methods (GET, HEAD, OPTIONS, and TRACE). 您可以使用AutoValidateAntiforgeryToken屬性,而不是廣泛套用 ValidateAntiForgeryToken 屬性,然後使用 IgnoreAntiforgeryToken 屬性來覆寫它。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

我們建議您針對非 API 案例廣泛使用 AutoValidateAntiforgeryTokenWe recommend use of AutoValidateAntiforgeryToken broadly for non-API scenarios. 這可確保預設會保護 POST 動作。This ensures POST actions are protected by default. 替代方式是預設忽略 antiforgery token,除非將 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. 所有貼文都應該傳送 antiforgery token。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()));

覆寫全域或控制器 antiforgery 屬性Override global or controller antiforgery attributes

IgnoreAntiforgeryToken篩選器是用來消除指定動作(或控制器)的 antiforgery token 需求。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

使用者通過驗證之後,應該重新整理權杖,方法是將使用者重新導向至 view 或 Razor Pages 頁面。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 架構應用程式中,antiforgery 權杖會使用隱藏的表單欄位來傳遞至伺服器。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. 因此,使用本機儲存體將 antiforgery token 儲存在用戶端上,並以要求標頭的形式傳送權杖是建議的方法。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 搭配 views,可以從視圖內使用服務來建立權杖。Using JavaScript with views, the token can be created using a service from within the view. AspNetCore Antiforgery. IAntiforgery服務插入至視圖,並呼叫GetAndStoreTokensInject 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,或從用戶端讀取 cookie。This approach eliminates the need to deal directly with setting cookies from the server or reading them from the client.

上述範例會使用 JavaScript 來讀取 AJAX POST 標頭的隱藏欄位值。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的標頭中傳送權杖,請將 antiforgery 服務設定為尋找 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. 如果伺服器傳送名稱為 XSRF-TOKEN的 cookie,則 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:

  • 設定您的應用程式,以在稱為 XSRF-TOKEN的 cookie 中提供權杖。Configure your app to provide a token in a cookie called XSRF-TOKEN.
  • 設定 antiforgery 服務以尋找名為 X-XSRF-TOKEN的標頭。Configure 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)

擴充 antiforgeryExtend 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