防止 ASP.NET Core中的跨網站偽造要求 (XSRF/CSRF) 攻擊

作者: Fiyaz HasanRick AndersonSteve Smith

跨網站偽造要求 (也稱為 XSRF 或 CSRF) 是針對 Web 裝載應用程式的攻擊,惡意 Web 應用程式可能會影響用戶端瀏覽器與信任該瀏覽器的 Web 應用程式之間的互動。 這些攻擊是可行的,因為網頁瀏覽器會隨著每個要求自動傳送某些類型的驗證權杖給網站。 這種惡意探索形式也稱為 單鍵攻擊會話, 因為攻擊會利用使用者先前驗證的會話。

CSRF 攻擊的範例:

  1. 使用者使用表單驗證登入 www.good-banking-site.example.com 。 伺服器會驗證使用者,併發出包含驗證 cookie 的回應。 網站很容易遭受攻擊,因為它信任任何使用有效驗證 cookie 所接收的要求。

  2. 使用者造訪惡意網站 www.bad-crook-site.example.com

    惡意網站 www.bad-crook-site.example.com 包含類似下列範例的 HTML 表單:

    <h1>Congratulations! You're a Winner!</h1>
    <form action="https://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 到易受攻擊的網站,而不是惡意網站。 這是 CSRF 的「跨網站」部分。

  3. 使用者選取 [提交] 按鈕。 瀏覽器提出要求,並自動包含所要求網域的驗證 cookie 。 www.good-banking-site.example.com

  4. 要求會在具有使用者驗證內容的伺服器上執行 www.good-banking-site.example.com ,並可執行任何已驗證使用者允許執行的動作。

除了使用者選取提交表單按鈕的案例之外,惡意網站可能:

  • 執行自動提交表單的腳本。
  • 以 AJAX 要求形式傳送表單提交。
  • 使用 CSS 隱藏表單。

除了一開始流覽惡意網站以外,這些替代案例不需要來自使用者的任何動作或輸入。

使用 HTTPS 不會防止 CSRF 攻擊。 惡意網站可以 https://www.good-banking-site.com/ 像傳送不安全的要求一樣輕鬆傳送要求。

某些攻擊會以回應 GET 要求的端點為目標,在此情況下,可以使用影像標記來執行動作。 這種攻擊形式常見於允許影像但封鎖 JavaScript 的論壇網站上。 變更 GET 要求狀態的應用程式,其中變數或資源遭到改變,很容易遭受惡意攻擊。 變更狀態不安全的 GET 要求。 最佳做法是永遠不會變更 GET 要求的狀態。

針對用於 cookie 驗證的 Web 應用程式,可能會遭受 CSRF 攻擊,因為:

  • 瀏覽器會儲存 cookie 由 Web 應用程式發出的 。
  • 已 cookie 儲存的 包含已驗證使用者的會話 cookie 。
  • 瀏覽器會將與網域相關聯的所有 cookie 傳送至 Web 應用程式,不論應用程式在瀏覽器中產生要求的方式為何。

不過,CSRF 攻擊不限於惡意探索 cookie 。 例如,基本和摘要式驗證也很容易遭受攻擊。 當使用者使用基本或摘要式驗證登入之後,瀏覽器會自動傳送認證,直到會話結束為止。

在此內容中, 會話 是指使用者進行驗證的用戶端會話。 與伺服器端會話或ASP.NET Core會話中介軟體無關。

使用者可以採取預防措施來防範 CSRF 弱點:

  • 使用完 Web 應用程式時登出。
  • 定期清除瀏覽器 cookie 。

不過,CSRF 弱點基本上是 Web 應用程式的問題,而不是使用者。

驗證基本概念

Cookie型驗證是熱門的驗證形式。 權杖型驗證系統在熱門程度增加,特別是單頁應用程式 (SPA) 。

當使用者使用其使用者名稱和密碼進行驗證時,會發出權杖,其中包含可用於驗證和授權的驗證票證。 權杖會儲存為 cookie 隨用戶端提出之每個要求一起傳送的 。 產生和驗證此 cookie 動作是由 Cookie 驗證中介軟體執行。 中介軟體會將使用者主體序列化為加密的 cookie 。 在後續的要求中,中介軟體會 cookie 驗證 、重新建立主體,並將主體指派給 HttpContext.User 屬性。

權杖式驗證

當使用者通過驗證時,系統會將權杖發行 (而不是反分叉權杖) 。 權杖包含 宣告 形式的使用者資訊,或參考權杖,將應用程式指向應用程式所維護的使用者狀態。 當使用者嘗試存取需要驗證的資源時,權杖會以持有人權杖的形式傳送至具有額外授權標頭的應用程式。 此方法可讓應用程式變成無狀態。 在每個後續要求中,權杖會傳入伺服器端驗證的要求中。 此權杖未 加密;它是 編碼的。 在伺服器上,權杖會解碼以存取其資訊。 若要在後續要求上傳送權杖,請將權杖儲存在瀏覽器的本機儲存體中。 將權杖放在瀏覽器本機儲存體中並擷取權杖,並將其當做持有人權杖使用,可防範 CSRF 攻擊。 不過,如果應用程式很容易透過 XSS 或遭入侵的外部 JAVAscript 檔案插入腳本,攻擊者可以從本機儲存體擷取任何值,並將其傳送給自己。 ASP.NET Core預設會將變數的所有伺服器端輸出編碼,以減少 XSS 的風險。 如果您使用 Html.Raw 或自訂程式碼搭配不受信任的輸入來覆寫此行為,則可能會增加 XSS 的風險。

如果權杖儲存在瀏覽器的本機儲存體中,請不要擔心 CSRF 弱點。 當令牌儲存在 中 cookie 時,CSRF 是一個問題。 如需詳細資訊,請參閱 GitHub 問題 SPA 程式 代碼範例新增兩 cookie 個

裝載于一個網域的多個應用程式

共用主機環境很容易遭受會話攔截、登入 CSRF 和其他攻擊。

雖然 example1.contoso.netexample2.contoso.net 是不同的主機,但網域下的 *.contoso.net 主機之間有隱含信任關係。 此隱含信任關係可讓可能不受信任的主機影響彼此 cookie (控管 AJAX 要求的相同原始來源原則不一定適用于 HTTP cookie) 。

在裝載于相同網域的應用程式之間惡意探索受信任 cookie 攻擊,可以藉由不共用網域來防止攻擊。 當每個應用程式裝載于自己的網域時,不會有隱含 cookie 的信任關係可供惡意探索。

ASP.NET Core中的反分叉

警告

ASP.NET Core使用ASP.NET Core 資料保護來實作反詐騙。 資料保護堆疊必須設定為在伺服器陣列中運作。 如需詳細資訊,請參閱 設定資料保護

在 中 Program.cs 呼叫下列其中一個 API 時,反交錯中介軟體會新增至相依性插入容器:

FormTagHelper 會將防偽權杖插入 HTML 表單元素中。 檔案中的 Razor 下列標記會自動產生反分叉權杖:

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

同樣地,如果表單的 方法不是 GET, IHtmlHelper.BeginForm 則預設會產生反Forgery 權杖。

當標籤包含 method="post" 屬性且下列任一項成立時 <form> ,就會自動產生 HTML 表單專案的反Forgery 標記:

  • 動作屬性是空的 (action="") 。
  • 動作屬性未提供 (<form method="post">) 。

可以停用自動為 HTML 表單專案產生反Forgery 權杖:

  • 使用 asp-antiforgery 屬性明確停用反Forgery 權杖:

    <form method="post" asp-antiforgery="false">
        <!-- ... -->
    </form>
    
  • 表單元素是使用標籤協助程式退出宣告標籤協助程式 ! 退出宣告符號

    <!form method="post">
        <!-- ... -->
    </!form>
    
  • FormTagHelper從檢視中移除 。 FormTagHelper您可以將下列指示詞新增至 Razor 檢視,以從檢視中移除 :

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

注意

Razor 頁面會自動受到 XSRF/CSRF 的保護。 如需詳細資訊,請參閱 XSRF/CSRF 和 Razor 頁面

防禦 CSRF 攻擊最常見的方法是使用 同步器權杖模式 (STP) 。 當使用者要求具有表單資料的頁面時,會使用 STP:

  1. 伺服器會將與目前使用者身分識別相關聯的權杖傳送給用戶端。
  2. 用戶端會將權杖傳回伺服器以進行驗證。
  3. 如果伺服器收到不符合已驗證使用者身分識別的權杖,則會拒絕要求。

權杖是唯一且無法預測的。 權杖也可以用來確保一系列要求 (的適當排序,例如確保要求順序為:第 1 > 頁 2 > 頁 3) 。 ASP.NET Core MVC 和 Razor Pages 範本中的所有表單都會產生反移轉權杖。 下列一組檢視範例會產生反移轉權杖:

<form asp-action="Index" asp-controller="Home" method="post">
    <!-- ... -->
</form>

@using (Html.BeginForm("Index", "Home"))
{
    <!-- ... -->
}

使用 HTML 協助程式使用標籤協助程式 @Html.AntiForgeryToken ,明確地將反分叉標記新增至 <form> 元素:

<form asp-action="Index" asp-controller="Home" method="post">
    @Html.AntiForgeryToken()

    <!-- ... -->
</form>

在上述每個案例中,ASP.NET Core新增類似下列範例的隱藏表單欄位:

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

ASP.NET Core包含三個篩選準則,可用於反forgery 權杖:

使用 AddControllers 的 Antiforgery

呼叫 AddControllers不會 啟用反forgery 權杖。 AddControllersWithViews 必須呼叫 ,才能支援內建的反移轉權杖。

多個瀏覽器索引標籤和同步器權杖模式

使用同步器權杖模式時,只有最近載入的頁面包含有效的反移轉權杖。 使用多個索引標籤可能會有問題。 例如,如果使用者開啟多個索引標籤:

  • 只有最近載入的索引標籤包含有效的反移轉權杖。
  • 從先前載入的索引標籤提出的要求失敗,並出現錯誤: Antiforgery token validation failed. The antiforgery cookie token and request token do not match

如果這造成問題,請考慮替代 CSRF 保護模式。

使用 設定反分叉 AntiforgeryOptions

在 中 Program.cs 自訂 AntiforgeryOptions

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

使用 類別的屬性 CookieBuilder 設定反分叉 Cookie 屬性,如下表所示。

選項 Description
Cookie 決定用來建立反分叉 cookie 的設定。
FormFieldName 反forgery 系統用來在檢視中轉譯反forgery 標記的隱藏表單欄位名稱。
HeaderName 反分叉系統所使用的標頭名稱。 如果 null 為 ,則系統只會考慮表單資料。
SuppressXFrameOptionsHeader 指定是否要隱藏標頭的 X-Frame-Options 產生。 根據預設,標頭會產生值為 「SAMEORIGIN」。 預設值為 false

如需詳細資訊,請參閱CookieAuthenticationOptions

使用 產生反forgery 權杖 IAntiforgery

IAntiforgery 提供 API 來設定反移轉功能。 IAntiforgery 可以使用 來 Program.csWebApplication.Services 要求 。 下列範例會使用來自應用程式首頁的中介軟體來產生反分叉權杖,並在回應中將其傳送為 cookie :

app.UseRouting();

app.UseAuthorization();

var antiforgery = app.Services.GetRequiredService<IAntiforgery>();

app.Use((context, next) =>
{
    var requestPath = context.Request.Path.Value;

    if (string.Equals(requestPath, "/", StringComparison.OrdinalIgnoreCase)
        || string.Equals(requestPath, "/index.html", StringComparison.OrdinalIgnoreCase))
    {
        var tokenSet = antiforgery.GetAndStoreTokens(context);
        context.Response.Cookies.Append("XSRF-TOKEN", tokenSet.RequestToken!,
            new CookieOptions { HttpOnly = false });
    }

    return next(context);
});

上述範例會設定 cookie 名為 XSRF-TOKEN 的 。 用戶端可以讀取此內容 cookie ,並將其值當作附加至 AJAX 要求的標頭提供。 例如,Angular包含預設讀取具名 XSRF-TOKEN 的 cookie內建 XSRF 保護

需要反forgery 驗證

ValidateAntiForgeryToken動作篩選準則可以套用至個別動作、控制器或全域。 除非要求包含有效的反forgery 權杖,否則對套用此篩選動作的要求會遭到封鎖:

[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult Index()
{
    // ...

    return RedirectToAction();
}

屬性 ValidateAntiForgeryToken 需要權杖來要求其標記的動作方法,包括 HTTP GET 要求。 ValidateAntiForgeryToken如果屬性套用到應用程式的控制器上,則可以使用 IgnoreAntiforgeryToken 屬性覆寫它。

僅針對不安全的 HTTP 方法自動驗證反分叉權杖

您可以使用AutoValidateAntiforgeryToken屬性,而不是廣泛套用 ValidateAntiForgeryToken 屬性,然後使用 IgnoreAntiforgeryToken 屬性覆寫它。 此屬性的運作方式 ValidateAntiForgeryToken 與 屬性相同,不同之處在于它不需要使用下列 HTTP 方法提出要求的權杖:

  • GET
  • HEAD
  • OPTIONS
  • TRACE

建議您廣泛地針對非 API 案例使用 AutoValidateAntiforgeryToken 。 此屬性可確保 POST 動作預設受到保護。 除非套用至個別動作方法, ValidateAntiForgeryToken 否則替代方法預設會忽略反移轉權杖。 在此案例中,POST 動作方法可能會因錯誤而不受保護,讓應用程式容易遭受 CSRF 攻擊。 所有 POST 都應該傳送反移轉權杖。

API 沒有自動機制可傳送非 cookie 部分的權杖。 實作可能取決於用戶端程式代碼實作。 以下顯示一些範例:

類別層級範例:

[AutoValidateAntiforgeryToken]
public class HomeController : Controller

全域範例:

builder.Services.AddControllersWithViews(options =>
{
    options.Filters.Add(new AutoValidateAntiforgeryTokenAttribute());
});

覆寫全域或控制器反forgery 屬性

IgnoreAntiforgeryToken篩選準則可用來消除指定動作的反forgery 權杖 (或控制器) 的需求。 套用時,此篩選會 ValidateAntiForgeryToken 全域或控制器) 覆寫在 AutoValidateAntiforgeryToken 較高層級 (指定的篩選。

[IgnoreAntiforgeryToken]
public IActionResult IndexOverride()
{
    // ...

    return RedirectToAction();
}

驗證之後重新整理權杖

權杖應在使用者驗證之後重新整理,方法是將使用者重新導向至檢視或 Razor 頁面頁面。

JavaScript、AJAX 和 SPA

在傳統 HTML 型應用程式中,反移轉權杖會使用隱藏的表單欄位傳遞至伺服器。 在現代 JavaScript 型應用程式和 SPA 中,許多要求都是以程式設計方式進行。 這些 AJAX 要求可能會使用其他技術 (,例如要求標頭或 cookie s) 來傳送權杖。

如果使用 cookie s 來儲存驗證權杖,以及在伺服器上驗證 API 要求,CSRF 是潛在的問題。 如果使用本機儲存體來儲存權杖,CSRF 弱點可能會降低,因為本機儲存體的值不會隨著每個要求自動傳送到伺服器。 建議使用本機儲存體將反移轉權杖儲存在用戶端上,並將權杖傳送為要求標頭。

JavaScript

使用 JavaScript 搭配檢視時,可以使用檢視內的服務來建立權杖。 將 IAntiforgery 服務插入檢視並呼叫 GetAndStoreTokens

@inject Microsoft.AspNetCore.Antiforgery.IAntiforgery Antiforgery

@{
    ViewData["Title"] = "JavaScript";

    var requestToken = Antiforgery.GetAndStoreTokens(Context).RequestToken;
}

<input id="RequestVerificationToken" type="hidden" value="@requestToken" />

<button id="button" class="btn btn-primary">Submit with Token</button>
<div id="result" class="mt-2"></div>

@section Scripts {
<script>
    document.addEventListener("DOMContentLoaded", () => {
        const resultElement = document.getElementById("result");

        document.getElementById("button").addEventListener("click", async () => {

            const response = await fetch("@Url.Action("FetchEndpoint")", {
                method: "POST",
                headers: {
                    RequestVerificationToken:
                        document.getElementById("RequestVerificationToken").value
                }
            });

            if (response.ok) {
                resultElement.innerText = await response.text();
            } else {
                resultElement.innerText = `Request Failed: ${response.status}`
            }
        });
    });
</script>
}

上述範例會使用 JavaScript 來讀取 AJAX POST 標頭的隱藏欄位值。

這種方法不需要直接從伺服器處理設定 cookie ,或從用戶端讀取設定。 不過,無法插入 IAntiforgery 服務時,請使用 JavaScript 來存取 中的權杖 cookie :

  • 存取伺服器額外要求中的權杖,通常是 same-origin
  • cookie使用 的內容來建立具有權杖值的標頭。

假設腳本會在名為 X-XSRF-TOKEN 的要求標頭中傳送權杖,請設定 antiforgery 服務來尋找 X-XSRF-TOKEN 標頭:

builder.Services.AddAntiforgery(options => options.HeaderName = "X-XSRF-TOKEN");

下列範例會將要求權杖寫入 JavaScript 可 cookie 讀取的受保護端點:

app.UseAuthorization();
app.MapGet("antiforgery/token", (IAntiforgery forgeryService, HttpContext context) =>
{
    var tokens = forgeryService.GetAndStoreTokens(context);
    context.Response.Cookies.Append("XSRF-TOKEN", tokens.RequestToken!,
            new CookieOptions { HttpOnly = false });

    return Results.Ok();
}).RequireAuthorization();

下列範例會使用 JavaScript 提出 AJAX 要求來取得權杖,並使用適當的標頭提出另一個要求:

var response = await fetch("/antiforgery/token", {
    method: "GET",
    headers: { "Authorization": authorizationToken }
});

if (response.ok) {
    // https://developer.mozilla.org/docs/web/api/document/cookie
    const xsrfToken = document.cookie
        .split("; ")
        .find(row => row.startsWith("XSRF-TOKEN="))
        .split("=")[1];

    response = await fetch("/JavaScript/FetchEndpoint", {
        method: "POST",
        headers: { "X-XSRF-TOKEN": xsrfToken, "Authorization": authorizationToken }
    });

    if (response.ok) {
        resultElement.innerText = await response.text();
    } else {
        resultElement.innerText = `Request Failed: ${response.status}`
    }
} else {    
    resultElement.innerText = `Request Failed: ${response.status}`
}

具有最小 API 的反移轉

Minimal APIs 不支援使用包含的篩選 (ValidateAntiForgeryTokenAutoValidateAntiforgeryTokenIgnoreAntiforgeryToken) , IAntiforgery 但會提供必要的 API 來驗證要求。

下列範例會建立一個篩選準則,以驗證反分叉權杖:

internal static class AntiForgeryExtensions
{
    public static TBuilder ValidateAntiforgery<TBuilder>(this TBuilder builder) where TBuilder : IEndpointConventionBuilder
    {
        return builder.AddEndpointFilter(routeHandlerFilter: async (context, next) =>
        {
            try
            {
                var antiForgeryService = context.HttpContext.RequestServices.GetRequiredService<IAntiforgery>();
                await antiForgeryService.ValidateRequestAsync(context.HttpContext);
            }
            catch (AntiforgeryValidationException)
            {
                return Results.BadRequest("Antiforgery token validation failed.");
            }

            return await next(context);

        });
    }
}

篩選準則接著可以套用至端點:

app.MapPost("api/upload", (IFormFile name) => Results.Accepted())
    .RequireAuthorization()
    .ValidateAntiforgery();

Windows 驗證和反forgery cookie s

使用 Windows 驗證時,應用程式端點必須受到保護,以防止 CSRF 攻擊的方式與針對 cookie s 完成的方式相同。 瀏覽器會隱含地將驗證內容傳送至伺服器和端點,才能受到 CSRF 攻擊的保護。

擴充反分叉

IAntiforgeryAdditionalDataProvider 類型可讓開發人員在每個權杖中來回傳回其他資料,以擴充反 CSRF 系統的行為。 GetAdditionalData每次產生欄位權杖時都會呼叫 方法,而傳回值會內嵌在產生的權杖中。 實作者可以傳回時間戳記、nonce 或任何其他值,然後在驗證權杖時呼叫 ValidateAdditionalData 來驗證此資料。 用戶端的使用者名稱已經內嵌在產生的權杖中,因此不需要包含這項資訊。 如果權杖包含補充資料但未 IAntiForgeryAdditionalDataProvider 設定,則不會驗證補充資料。

其他資源

跨網站偽造要求 (也稱為 XSRF 或 CSRF) 是針對 Web 裝載應用程式的攻擊,惡意 Web 應用程式可能會影響用戶端瀏覽器與信任該瀏覽器的 Web 應用程式之間的互動。 這些攻擊是可行的,因為網頁瀏覽器會隨著每個要求自動傳送某些類型的驗證權杖給網站。 這種惡意探索形式也稱為 單鍵攻擊會話, 因為攻擊會利用使用者先前驗證的會話。

CSRF 攻擊的範例:

  1. 使用者使用表單驗證登入 www.good-banking-site.example.com 。 伺服器會驗證使用者,併發出包含驗證 cookie 的回應。 網站很容易遭受攻擊,因為它信任任何使用有效驗證 cookie 所接收的要求。

  2. 使用者造訪惡意網站 www.bad-crook-site.example.com

    惡意網站 www.bad-crook-site.example.com 包含類似下列範例的 HTML 表單:

    <h1>Congratulations! You're a Winner!</h1>
    <form action="https://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 到易受攻擊的網站,而不是惡意網站。 這是 CSRF 的「跨網站」部分。

  3. 使用者選取 [提交] 按鈕。 瀏覽器提出要求,並自動包含所要求網域的驗證 cookie 。 www.good-banking-site.example.com

  4. 要求會在具有使用者驗證內容的伺服器上執行 www.good-banking-site.example.com ,並可執行任何已驗證使用者允許執行的動作。

除了使用者選取提交表單按鈕的案例之外,惡意網站可能:

  • 執行自動提交表單的腳本。
  • 以 AJAX 要求形式傳送表單提交。
  • 使用 CSS 隱藏表單。

除了一開始流覽惡意網站以外,這些替代案例不需要來自使用者的任何動作或輸入。

使用 HTTPS 不會防止 CSRF 攻擊。 惡意網站可以 https://www.good-banking-site.com/ 像傳送不安全的要求一樣輕鬆傳送要求。

某些攻擊會以回應 GET 要求的端點為目標,在此情況下,可以使用影像標記來執行動作。 這種攻擊形式常見於允許影像但封鎖 JavaScript 的論壇網站上。 變更 GET 要求狀態的應用程式,其中變數或資源遭到改變,很容易遭受惡意攻擊。 變更狀態不安全的 GET 要求。 最佳做法是永遠不會變更 GET 要求的狀態。

針對用於 cookie 驗證的 Web 應用程式,可能會遭受 CSRF 攻擊,因為:

  • 瀏覽器會儲存 cookie 由 Web 應用程式發出的 。
  • 已 cookie 儲存的 包含已驗證使用者的會話 cookie 。
  • 瀏覽器會將與網域相關聯的所有 cookie 傳送至 Web 應用程式,不論應用程式在瀏覽器中產生要求的方式為何。

不過,CSRF 攻擊不限於惡意探索 cookie 。 例如,基本和摘要式驗證也很容易遭受攻擊。 當使用者使用基本或摘要式驗證登入之後,瀏覽器會自動傳送認證,直到會話結束為止。

在此內容中, 會話 是指使用者進行驗證的用戶端會話。 與伺服器端會話或ASP.NET Core會話中介軟體無關。

使用者可以採取預防措施來防範 CSRF 弱點:

  • 使用完 Web 應用程式時登出。
  • 定期清除瀏覽器 cookie 。

不過,CSRF 弱點基本上是 Web 應用程式的問題,而不是使用者。

驗證基本概念

Cookie型驗證是熱門的驗證形式。 權杖型驗證系統在熱門程度增加,特別是單頁應用程式 (SPA) 。

當使用者使用其使用者名稱和密碼進行驗證時,會發出權杖,其中包含可用於驗證和授權的驗證票證。 權杖會儲存為 cookie 隨用戶端提出之每個要求一起傳送的 。 產生和驗證此 cookie 動作是由 Cookie 驗證中介軟體執行。 中介軟體會將使用者主體序列化為加密的 cookie 。 在後續的要求中,中介軟體會 cookie 驗證 、重新建立主體,並將主體指派給 HttpContext.User 屬性。

權杖式驗證

當使用者通過驗證時,系統會將權杖發行 (而不是反分叉權杖) 。 權杖包含 宣告 形式的使用者資訊,或參考權杖,將應用程式指向應用程式所維護的使用者狀態。 當使用者嘗試存取需要驗證的資源時,權杖會以持有人權杖的形式傳送至具有額外授權標頭的應用程式。 此方法可讓應用程式變成無狀態。 在每個後續要求中,權杖會傳入伺服器端驗證的要求中。 此權杖未 加密;它是 編碼的。 在伺服器上,權杖會解碼以存取其資訊。 若要在後續要求上傳送權杖,請將權杖儲存在瀏覽器的本機儲存體中。 如果權杖儲存在瀏覽器的本機儲存體中,請不要擔心 CSRF 弱點。 當令牌儲存在 中 cookie 時,CSRF 是一個問題。 如需詳細資訊,請參閱 GitHub 問題 SPA 程式 代碼範例新增兩 cookie 個

裝載于一個網域的多個應用程式

共用主機環境很容易遭受會話攔截、登入 CSRF 和其他攻擊。

雖然 example1.contoso.netexample2.contoso.net 是不同的主機,但網域下的 *.contoso.net 主機之間有隱含信任關係。 此隱含信任關係可讓可能不受信任的主機影響彼此 cookie (控管 AJAX 要求的相同原始來源原則不一定適用于 HTTP cookie) 。

在裝載于相同網域的應用程式之間惡意探索受信任 cookie 攻擊,可以藉由不共用網域來防止攻擊。 當每個應用程式裝載于自己的網域時,不會有隱含 cookie 的信任關係可供惡意探索。

ASP.NET Core中的反分叉

警告

ASP.NET Core使用ASP.NET Core 資料保護來實作反詐騙。 資料保護堆疊必須設定為在伺服器陣列中運作。 如需詳細資訊,請參閱 設定資料保護

在 中 Program.cs 呼叫下列其中一個 API 時,反交錯中介軟體會新增至相依性插入容器:

FormTagHelper 會將防偽權杖插入 HTML 表單元素中。 檔案中的 Razor 下列標記會自動產生反分叉權杖:

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

同樣地,如果表單的 方法不是 GET, IHtmlHelper.BeginForm 則預設會產生反Forgery 權杖。

當標籤包含 method="post" 屬性且下列任一項成立時 <form> ,就會自動產生 HTML 表單專案的反Forgery 標記:

  • 動作屬性是空的 (action="") 。
  • 動作屬性未提供 (<form method="post">) 。

可以停用自動為 HTML 表單專案產生反Forgery 權杖:

  • 使用 asp-antiforgery 屬性明確停用反Forgery 權杖:

    <form method="post" asp-antiforgery="false">
        <!-- ... -->
    </form>
    
  • 表單元素是使用標籤協助程式退出宣告標籤協助程式 ! 退出宣告符號

    <!form method="post">
        <!-- ... -->
    </!form>
    
  • FormTagHelper從檢視中移除 。 FormTagHelper您可以將下列指示詞新增至 Razor 檢視,以從檢視中移除 :

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

注意

Razor 頁面會自動受到 XSRF/CSRF 的保護。 如需詳細資訊,請參閱 XSRF/CSRF 和 Razor 頁面

防禦 CSRF 攻擊最常見的方法是使用 同步器權杖模式 (STP) 。 當使用者要求具有表單資料的頁面時,會使用 STP:

  1. 伺服器會將與目前使用者身分識別相關聯的權杖傳送給用戶端。
  2. 用戶端會將權杖傳回伺服器以進行驗證。
  3. 如果伺服器收到不符合已驗證使用者身分識別的權杖,則會拒絕要求。

權杖是唯一且無法預測的。 權杖也可以用來確保一系列要求 (的適當排序,例如確保要求順序:第 1 > 頁 2 > 頁 3) 。 ASP.NET Core MVC 和 Razor Pages 範本中的所有表單都會產生反Forgery 權杖。 下列一對檢視範例會產生反分叉權杖:

<form asp-action="Index" asp-controller="Home" method="post">
    <!-- ... -->
</form>

@using (Html.BeginForm("Index", "Home"))
{
    <!-- ... -->
}

在不使用標籤協助程式搭配 HTML 協助程式 @Html.AntiForgeryToken 的情況下,明確地將反Forgery 權杖新增至 <form> 元素:

<form asp-action="Index" asp-controller="Home" method="post">
    @Html.AntiForgeryToken()

    <!-- ... -->
</form>

在上述每個案例中,ASP.NET Core新增類似下列範例的隱藏表單欄位:

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

ASP.NET Core包含三個篩選準則,可用於反分叉權杖:

使用 AddControllers 的 Antiforgery

呼叫 AddControllers不會 啟用反Forgery 權杖。 AddControllersWithViews 必須呼叫 ,才能支援內建的反分叉權杖。

多個瀏覽器索引標籤和同步器權杖模式

使用同步器權杖模式時,只有最近載入的頁面包含有效的反Forgery 權杖。 使用多個索引標籤可能會有問題。 例如,如果使用者開啟多個索引標籤:

  • 只有最近載入的索引標籤包含有效的反分叉權杖。
  • 從先前載入的索引標籤提出的要求失敗,並出現錯誤: Antiforgery token validation failed. The antiforgery cookie token and request token do not match

如果這會造成問題,請考慮替代 CSRF 保護模式。

使用 設定反分叉 AntiforgeryOptions

在 中 Program.cs 自訂 AntiforgeryOptions

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

使用 類別的屬性 CookieBuilder 來設定反分叉 Cookie 屬性,如下表所示。

選項 Description
Cookie 決定用來建立反分 cookie 叉的設定。
FormFieldName 反forgery 系統用來在檢視中呈現反Forgery 權杖的隱藏表單欄位名稱。
HeaderName 反分叉系統所使用的標頭名稱。 如果 null 為 ,則系統只會考慮表單資料。
SuppressXFrameOptionsHeader 指定是否要隱藏標頭的 X-Frame-Options 產生。 根據預設,標頭會產生值為 「SAMEORIGIN」。 預設值為 false

如需詳細資訊,請參閱CookieAuthenticationOptions

使用 產生反分叉權杖 IAntiforgery

IAntiforgery 提供 API 來設定反Forgery 功能。 IAntiforgery 可以使用 來 Program.csWebApplication.Services 要求 。 下列範例會使用來自應用程式首頁的中介軟體來產生反分叉權杖,並在回應中將其傳送為 cookie :

app.UseRouting();

app.UseAuthorization();

var antiforgery = app.Services.GetRequiredService<IAntiforgery>();

app.Use((context, next) =>
{
    var requestPath = context.Request.Path.Value;

    if (string.Equals(requestPath, "/", StringComparison.OrdinalIgnoreCase)
        || string.Equals(requestPath, "/index.html", StringComparison.OrdinalIgnoreCase))
    {
        var tokenSet = antiforgery.GetAndStoreTokens(context);
        context.Response.Cookies.Append("XSRF-TOKEN", tokenSet.RequestToken!,
            new CookieOptions { HttpOnly = false });
    }

    return next(context);
});

上述範例會設定 cookie 名為 XSRF-TOKEN 的 。 用戶端可以讀取此內容 cookie ,並將其值當作附加至 AJAX 要求的標頭提供。 例如,Angular包含預設讀取 cookie 具名 XSRF-TOKEN內建 XSRF 保護

需要反分叉驗證

ValidateAntiForgeryToken動作篩選可以套用至個別動作、控制器或全域。 除非要求包含有效的反forgery 權杖,否則對套用此篩選動作的要求會遭到封鎖:

[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult Index()
{
    // ...

    return RedirectToAction();
}

屬性 ValidateAntiForgeryToken 需要權杖來要求其標記的動作方法,包括 HTTP GET 要求。 ValidateAntiForgeryToken如果屬性套用至應用程式的控制器,可以使用 屬性覆寫 IgnoreAntiforgeryToken

僅針對不安全的 HTTP 方法自動驗證反分叉權杖

您可以使用AutoValidateAntiforgeryToken屬性,而不是廣泛套用 ValidateAntiForgeryToken 屬性,然後以屬性覆 IgnoreAntiforgeryToken 寫該屬性。 此屬性的運作方式 ValidateAntiForgeryToken 與 屬性相同,不同之處在于它不需要權杖來取得使用下列 HTTP 方法提出的要求:

  • GET
  • HEAD
  • OPTIONS
  • TRACE

建議您廣泛地針對非 API 案例使用 AutoValidateAntiforgeryToken 。 此屬性可確保 POST 動作預設受到保護。 除非套用至個別動作方法, ValidateAntiForgeryToken 否則替代方法是預設忽略反分機權杖。 在此案例中,POST 動作方法可能會遭到錯誤保護,讓應用程式容易遭受 CSRF 攻擊。 所有 POST 都應該傳送反分叉權杖。

API 沒有自動機制可傳送非 cookie 部分的權杖。 實作可能取決於用戶端程式代碼實作。 以下顯示一些範例:

類別層級範例:

[AutoValidateAntiforgeryToken]
public class HomeController : Controller

全域範例:

builder.Services.AddControllersWithViews(options =>
{
    options.Filters.Add(new AutoValidateAntiforgeryTokenAttribute());
});

覆寫全域或控制器反分叉屬性

IgnoreAntiforgeryToken篩選準則可用來消除指定動作 (或控制器) 的反forgery 權杖需求。 套用時,此篩選會 ValidateAntiForgeryToken 覆寫和 AutoValidateAntiforgeryToken 篩選在全域或控制器) 上指定于較高層級 (。

[IgnoreAntiforgeryToken]
public IActionResult IndexOverride()
{
    // ...

    return RedirectToAction();
}

驗證後重新整理權杖

在使用者通過驗證之後,應該重新整理權杖,方法是將使用者重新導向至檢視或 Razor 頁面頁面。

JavaScript、AJAX 和 SPA

在傳統 HTML 型應用程式中,反分叉權杖會使用隱藏的表單欄位傳遞至伺服器。 在以新式 JavaScript 為基礎的應用程式和 SPA 中,許多要求都是以程式設計方式提出。 這些 AJAX 要求可能會使用其他技術 (,例如要求標頭或 cookie s) 來傳送權杖。

如果使用 cookie 來儲存驗證權杖,以及在伺服器上驗證 API 要求,CSRF 是潛在的問題。 如果使用本機儲存體來儲存權杖,CSRF 弱點可能會降低,因為本機儲存體的值不會隨著每個要求自動傳送到伺服器。 建議使用本機儲存體在用戶端上儲存反Forgery 權杖,並將權杖傳送為要求標頭。

JavaScript

使用 JavaScript 搭配檢視時,可以使用檢視中的服務來建立權杖。 將 IAntiforgery 服務插入檢視,並呼叫 GetAndStoreTokens

@inject Microsoft.AspNetCore.Antiforgery.IAntiforgery Antiforgery

@{
    ViewData["Title"] = "JavaScript";

    var requestToken = Antiforgery.GetAndStoreTokens(Context).RequestToken;
}

<input id="RequestVerificationToken" type="hidden" value="@requestToken" />

<button id="button" class="btn btn-primary">Submit with Token</button>
<div id="result" class="mt-2"></div>

@section Scripts {
<script>
    document.addEventListener("DOMContentLoaded", () => {
        const resultElement = document.getElementById("result");

        document.getElementById("button").addEventListener("click", async () => {

            const response = await fetch("@Url.Action("FetchEndpoint")", {
                method: "POST",
                headers: {
                    RequestVerificationToken:
                        document.getElementById("RequestVerificationToken").value
                }
            });

            if (response.ok) {
                resultElement.innerText = await response.text();
            } else {
                resultElement.innerText = `Request Failed: ${response.status}`
            }
        });
    });
</script>
}

上述範例會使用 JavaScript 讀取 AJAX POST 標頭的隱藏欄位值。

這種方法不需要直接從伺服器處理 設定 cookie ,或從用戶端讀取它們。 不過,無法插入 IAntiforgery 服務時,JavaScript 也可以存取 中的 cookie 權杖,從伺服器的其他要求取得, (通常 same-origin) ,並使用 cookie 的內容來建立具有權杖值的標頭。

假設腳本會在稱為 X-XSRF-TOKEN 的要求標頭中傳送權杖,請設定反forgery 服務來尋找 X-XSRF-TOKEN 標頭:

builder.Services.AddAntiforgery(options => options.HeaderName = "X-XSRF-TOKEN");

下列範例會新增將要求權杖寫入 JavaScript 可 cookie 讀取的受保護端點:

app.UseAuthorization();
app.MapGet("antiforgery/token", (IAntiforgery forgeryService, HttpContext context) =>
{
    var tokens = forgeryService.GetAndStoreTokens(context);
    context.Response.Cookies.Append("XSRF-TOKEN", tokens.RequestToken!,
            new CookieOptions { HttpOnly = false });

    return Results.Ok();
}).RequireAuthorization();

下列範例會使用 JavaScript 提出 AJAX 要求來取得權杖,並使用適當的標頭提出另一個要求:

var response = await fetch("/antiforgery/token", {
    method: "GET",
    headers: { "Authorization": authorizationToken }
});

if (response.ok) {
    // https://developer.mozilla.org/docs/web/api/document/cookie
    const xsrfToken = document.cookie
        .split("; ")
        .find(row => row.startsWith("XSRF-TOKEN="))
        .split("=")[1];

    response = await fetch("/JavaScript/FetchEndpoint", {
        method: "POST",
        headers: { "X-XSRF-TOKEN": xsrfToken, "Authorization": authorizationToken }
    });

    if (response.ok) {
        resultElement.innerText = await response.text();
    } else {
        resultElement.innerText = `Request Failed: ${response.status}`
    }
} else {    
    resultElement.innerText = `Request Failed: ${response.status}`
}

Windows 驗證和反forgery cookie s

使用 Windows 驗證時,應用程式端點必須受到保護,以防止 CSRF 攻擊的方式與針對 cookie s 執行的方式相同。 瀏覽器會隱含地將驗證內容傳送至伺服器,因此端點必須受到保護,以防止 CSRF 攻擊。

擴充反分叉

IAntiforgeryAdditionalDataProvider 類型可讓開發人員透過往返每個權杖中的其他資料,擴充反 CSRF 系統的行為。 GetAdditionalData每次產生欄位權杖時都會呼叫 方法,而且傳回值會內嵌在產生的權杖中。 實作者可以傳回時間戳記、nonce 或任何其他值,然後在驗證權杖時呼叫 ValidateAdditionalData 來驗證此資料。 用戶端的使用者名稱已內嵌在產生的權杖中,因此不需要包含這項資訊。 如果權杖包含補充資料,但未 IAntiForgeryAdditionalDataProvider 設定任何補充資料,則不會驗證補充資料。

其他資源

跨網站偽造要求 (也稱為 XSRF 或 CSRF) 是針對 Web 裝載應用程式的攻擊,惡意 Web 應用程式可能會影響用戶端瀏覽器與信任該瀏覽器的 Web 應用程式之間的互動。 這些攻擊是可行的,因為網頁瀏覽器會隨著每個要求自動傳送某些類型的驗證權杖給網站。 這種惡意探索形式也稱為 單鍵攻擊會話, 因為攻擊會利用使用者先前驗證的會話。

CSRF 攻擊的範例:

  1. 使用者使用表單驗證登入 www.good-banking-site.example.com 。 伺服器會驗證使用者,併發出包含驗證 cookie 的回應。 網站很容易遭受攻擊,因為它信任任何使用有效驗證 cookie 所接收的要求。

  2. 使用者造訪惡意網站 www.bad-crook-site.example.com

    惡意網站 www.bad-crook-site.example.com 包含類似下列範例的 HTML 表單:

    <h1>Congratulations! You're a Winner!</h1>
    <form action="https://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 到易受攻擊的網站,而不是惡意網站。 這是 CSRF 的「跨網站」部分。

  3. 使用者選取 [提交] 按鈕。 瀏覽器提出要求,並自動包含所要求網域的驗證 cookie 。 www.good-banking-site.example.com

  4. 要求會在具有使用者驗證內容的伺服器上執行 www.good-banking-site.example.com ,並可執行任何已驗證使用者允許執行的動作。

除了使用者選取提交表單按鈕的案例之外,惡意網站可能:

  • 執行自動提交表單的腳本。
  • 以 AJAX 要求形式傳送表單提交。
  • 使用 CSS 隱藏表單。

除了一開始流覽惡意網站以外,這些替代案例不需要來自使用者的任何動作或輸入。

使用 HTTPS 不會防止 CSRF 攻擊。 惡意網站可以 https://www.good-banking-site.com/ 像傳送不安全的要求一樣輕鬆傳送要求。

某些攻擊會以回應 GET 要求的端點為目標,在此情況下,可以使用影像標記來執行動作。 這種攻擊形式常見於允許影像但封鎖 JavaScript 的論壇網站上。 變更 GET 要求狀態的應用程式,其中變數或資源遭到改變,很容易遭受惡意攻擊。 變更狀態不安全的 GET 要求。 最佳做法是永遠不會變更 GET 要求的狀態。

針對用於 cookie 驗證的 Web 應用程式,可能會遭受 CSRF 攻擊,因為:

  • 瀏覽器會儲存 cookie 由 Web 應用程式發出的 。
  • 已 cookie 儲存的 包含已驗證使用者的會話 cookie 。
  • 瀏覽器會將與網域相關聯的所有 cookie 傳送至 Web 應用程式,不論應用程式在瀏覽器中產生要求的方式為何。

不過,CSRF 攻擊不限於惡意探索 cookie 。 例如,基本和摘要式驗證也很容易遭受攻擊。 當使用者使用基本或摘要式驗證登入之後,瀏覽器會自動傳送認證,直到會話結束為止。

在此內容中, 會話 是指使用者進行驗證的用戶端會話。 與伺服器端會話或ASP.NET Core會話中介軟體無關。

使用者可以採取預防措施來防範 CSRF 弱點:

  • 使用完 Web 應用程式時登出。
  • 定期清除瀏覽器 cookie 。

不過,CSRF 弱點基本上是 Web 應用程式的問題,而不是使用者。

驗證基本概念

Cookie型驗證是熱門的驗證形式。 權杖型驗證系統在熱門程度增加,特別是單頁應用程式 (SPA) 。

當使用者使用其使用者名稱和密碼進行驗證時,會發出權杖,其中包含可用於驗證和授權的驗證票證。 權杖會儲存為 cookie 隨用戶端提出之每個要求一起傳送的 。 產生和驗證此 cookie 動作是由 Cookie 驗證中介軟體執行。 中介軟體會將使用者主體序列化為加密的 cookie 。 在後續的要求中,中介軟體會 cookie 驗證 、重新建立主體,並將主體指派給 HttpContext.User 屬性。

權杖式驗證

當使用者通過驗證時,系統會將權杖發行 (而不是反分叉權杖) 。 權杖包含 宣告 形式的使用者資訊,或參考權杖,將應用程式指向應用程式所維護的使用者狀態。 當使用者嘗試存取需要驗證的資源時,權杖會以持有人權杖的形式傳送至具有額外授權標頭的應用程式。 此方法可讓應用程式變成無狀態。 在每個後續要求中,權杖會傳入伺服器端驗證的要求中。 此權杖未 加密;它是 編碼的。 在伺服器上,權杖會解碼以存取其資訊。 若要在後續要求上傳送權杖,請將權杖儲存在瀏覽器的本機儲存體中。 如果權杖儲存在瀏覽器的本機儲存體中,請不要擔心 CSRF 弱點。 當令牌儲存在 中 cookie 時,CSRF 是一個問題。 如需詳細資訊,請參閱 GitHub 問題 SPA 程式 代碼範例新增兩 cookie 個

裝載于一個網域的多個應用程式

共用主機環境很容易遭受會話攔截、登入 CSRF 和其他攻擊。

雖然 example1.contoso.netexample2.contoso.net 是不同的主機,但網域下的 *.contoso.net 主機之間有隱含信任關係。 此隱含信任關係可讓可能不受信任的主機影響彼此 cookie (控管 AJAX 要求的相同原始來源原則不一定適用于 HTTP cookie) 。

在裝載于相同網域的應用程式之間惡意探索受信任 cookie 攻擊,可以藉由不共用網域來防止攻擊。 當每個應用程式裝載于自己的網域時,不會有隱含 cookie 的信任關係可供惡意探索。

ASP.NET Core反分機設定

警告

ASP.NET Core使用ASP.NET Core 資料保護來實作反詐騙。 資料保護堆疊必須設定為在伺服器陣列中運作。 如需詳細資訊,請參閱 設定資料保護

在 中 Startup.ConfigureServices 呼叫下列其中一個 API 時,反交錯中介軟體會新增至相依性插入容器:

在 ASP.NET Core 2.0 或更新版本中,FormTagHelper會將反forgery 權杖插入 HTML 表單元素。 檔案中的 Razor 下列標記會自動產生反分叉權杖:

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

同樣地,如果表單的 方法不是 GET, IHtmlHelper.BeginForm 則預設會產生反Forgery 權杖。

當標籤包含 method="post" 屬性且下列任一項成立時 <form> ,就會自動產生 HTML 表單專案的反Forgery 標記:

  • 動作屬性是空的 (action="") 。
  • 動作屬性未提供 (<form method="post">) 。

可以停用自動為 HTML 表單專案產生反Forgery 權杖:

  • 使用 asp-antiforgery 屬性明確停用反Forgery 權杖:

    <form method="post" asp-antiforgery="false">
        ...
    </form>
    
  • 表單元素是使用標籤協助程式退出宣告標籤協助程式 ! 退出宣告符號

    <!form method="post">
        ...
    </!form>
    
  • FormTagHelper從檢視中移除 。 FormTagHelper您可以將下列指示詞新增至 Razor 檢視,以從檢視中移除 :

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

注意

Razor 頁面會自動受到 XSRF/CSRF 的保護。 如需詳細資訊,請參閱 XSRF/CSRF 和 Razor 頁面

防禦 CSRF 攻擊最常見的方法是使用 同步器權杖模式 (STP) 。 當使用者要求具有表單資料的頁面時,會使用 STP:

  1. 伺服器會將與目前使用者身分識別相關聯的權杖傳送給用戶端。
  2. 用戶端會將權杖傳回伺服器以進行驗證。
  3. 如果伺服器收到不符合已驗證使用者身分識別的權杖,則會拒絕要求。

權杖是唯一且無法預測的。 權杖也可以用來確保一系列要求 (的適當排序,例如確保要求順序:第 1 > 頁 2 > 頁 3) 。 ASP.NET Core MVC 和 Razor Pages 範本中的所有表單都會產生反Forgery 權杖。 下列一對檢視範例會產生反分叉權杖:

<form asp-controller="Todo" asp-action="Create" method="post">
    ...
</form>

@using (Html.BeginForm("Create", "Todo"))
{
    ...
}

在不使用標籤協助程式搭配 HTML 協助程式 @Html.AntiForgeryToken 的情況下,明確地將反Forgery 權杖新增至 <form> 元素:

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

在上述每個案例中,ASP.NET Core新增類似下列範例的隱藏表單欄位:

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

ASP.NET Core包含三個篩選準則,可用於反分叉權杖:

反分叉選項

在 中 Startup.ConfigureServices 自訂 AntiforgeryOptions

services.AddAntiforgery(options => 
{
    options.FormFieldName = "AntiforgeryFieldname";
    options.HeaderName = "X-CSRF-TOKEN-HEADERNAME";
    options.SuppressXFrameOptionsHeader = false;
});

使用 類別的屬性 CookieBuilder 來設定反分叉 Cookie 屬性,如下表所示。

選項 Description
Cookie 決定用來建立反分 cookie 叉的設定。
FormFieldName 反forgery 系統用來在檢視中呈現反Forgery 權杖的隱藏表單欄位名稱。
HeaderName 反分叉系統所使用的標頭名稱。 如果 null 為 ,則系統只會考慮表單資料。
SuppressXFrameOptionsHeader 指定是否要隱藏標頭的 X-Frame-Options 產生。 根據預設,標頭會產生值為 「SAMEORIGIN」。 預設值為 false

如需詳細資訊,請參閱CookieAuthenticationOptions

使用 IAntiforgery 設定反forgery 功能

IAntiforgery 提供 API 來設定反Forgery 功能。 IAntiforgery可以在 類別的 Startup 方法中 Configure 要求。

在下例中︰

  • 來自應用程式首頁的中介軟體可用來產生反分叉權杖,並在回應中以 的形式 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 是可套用至個別動作、控制器或全域的動作篩選。 除非要求包含有效的反Forgery 權杖,否則對套用此篩選動作的要求會遭到封鎖。

[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應用程式不會針對 GET、HEAD、OPTIONS 和 TRACE) (安全 HTTP 方法產生反分叉權杖。 您可以使用AutoValidateAntiforgeryToken屬性,而不是廣泛套用 ValidateAntiForgeryToken 屬性,然後以屬性覆 IgnoreAntiforgeryToken 寫該屬性。 此屬性的運作方式 ValidateAntiForgeryToken 與 屬性相同,不同之處在于它不需要權杖來取得使用下列 HTTP 方法提出的要求:

  • GET
  • HEAD
  • OPTIONS
  • TRACE

建議您廣泛地針對非 API 案例使用 AutoValidateAntiforgeryToken 。 此屬性可確保 POST 動作預設受到保護。 除非套用至個別動作方法, ValidateAntiForgeryToken 否則替代方法是預設忽略反分機權杖。 在此案例中,POST 動作方法可能會遭到錯誤保護,讓應用程式容易遭受 CSRF 攻擊。 所有 POST 都應該傳送反分叉權杖。

API 沒有自動機制可傳送非 cookie 部分的權杖。 實作可能取決於用戶端程式代碼實作。 以下顯示一些範例:

類別層級範例:

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

全域範例:

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

覆寫全域或控制器反分叉屬性

IgnoreAntiforgeryToken篩選準則可用來消除指定動作 (或控制器) 的反forgery 權杖需求。 套用時,此篩選會 ValidateAntiForgeryToken 覆寫和 AutoValidateAntiforgeryToken 篩選在全域或控制器) 上指定于較高層級 (。

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

驗證後重新整理權杖

在使用者通過驗證之後,應該重新整理權杖,方法是將使用者重新導向至檢視或 Razor 頁面頁面。

JavaScript、AJAX 和 SPA

在傳統 HTML 型應用程式中,反分叉權杖會使用隱藏的表單欄位傳遞至伺服器。 在以新式 JavaScript 為基礎的應用程式和 SPA 中,許多要求都是以程式設計方式提出。 這些 AJAX 要求可能會使用其他技術 (,例如要求標頭或 cookie s) 來傳送權杖。

如果使用 cookie 來儲存驗證權杖,以及在伺服器上驗證 API 要求,CSRF 是潛在的問題。 如果使用本機儲存體來儲存權杖,CSRF 弱點可能會降低,因為本機儲存體的值不會隨著每個要求自動傳送到伺服器。 建議使用本機儲存體在用戶端上儲存反Forgery 權杖,並將權杖傳送為要求標頭。

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>
</div>

這種方法不需要直接從伺服器處理 設定 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 的標頭中傳送權杖,請設定反forgery 服務來尋找 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

JSAngular會使用慣例來處理 CSRF。 如果伺服器傳送 cookie 名稱 XSRF-TOKEN 為 的 ,Angular JS$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;

        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);
    });
}

public void ConfigureServices(IServiceCollection services)
{
    services.AddAntiforgery(options => options.HeaderName = "X-XSRF-TOKEN");
}

Windows 驗證和反forgery cookie s

使用 Windows 驗證時,應用程式端點必須受到保護,以防止 CSRF 攻擊的方式與針對 cookie s 執行的方式相同。 瀏覽器會隱含地將驗證內容傳送至伺服器,因此端點必須受到保護,以防止 CSRF 攻擊。

擴充反分叉

IAntiforgeryAdditionalDataProvider 類型可讓開發人員透過往返每個權杖中的其他資料,擴充反 CSRF 系統的行為。 GetAdditionalData每次產生欄位權杖時都會呼叫 方法,而且傳回值會內嵌在產生的權杖中。 實作者可以傳回時間戳記、nonce 或任何其他值,然後在驗證權杖時呼叫 ValidateAdditionalData 來驗證此資料。 用戶端的使用者名稱已內嵌在產生的權杖中,因此不需要包含這項資訊。 如果權杖包含補充資料,但未 IAntiForgeryAdditionalDataProvider 設定任何補充資料,則不會驗證補充資料。

其他資源