ASP.NET Core 防止跨站請求偽造(XSRF/CSRF)攻擊


什么是反偽造攻擊?

跨站點請求偽造(也稱為XSRF或CSRF,發音為see-surf)是對Web托管應用程序的攻擊,因為惡意網站可能會影響客戶端瀏覽器和瀏覽器信任網站之間的交互。這種攻擊是完全有可能的,因為Web瀏覽器會自動在每一個請求中發送某些身份驗證令牌到請求網站。這種攻擊形式也被稱為 一鍵式攻擊會話控制 ,因為攻擊利用了用戶以前認證的會話。

CSRF攻擊的示例:

  1. 用戶登錄 www.example.com,使用表單身份驗證。
  2. 服務器對用戶進行身份驗證,並作出包含身份驗證Cookie的響應。
  3. 用戶訪問惡意網站。
    惡意網站包含類似於以下內容的HTML表單:
        <h1>You Are a Winner!</h1>
        <form action="http://example.com/api/account" method="post">
            <input type="hidden" name="Transaction" value="withdraw" />
            <input type="hidden" name="Amount" value="1000000" />
            <input type="submit" value="Click Me"/>
        </form>
請注意,表單的Action屬性將請求發送到易受攻擊的網站,而不是惡意網站。這是CSRF的“跨站點”部分。
  1. 用戶點擊提交按鈕,瀏覽器會自動包含請求站點(在這種情況下為易受攻擊的站點)的認證Cookie。
  2. 請求在擁有用戶身份驗證上下文的服務端運行,並且可以執行允許經過身份驗證用戶執行的任何操作。

此示例需要用戶單擊表單按鈕,惡意頁面也可以通過以下方式:

  • 自動運行提交表單的腳本。
  • 通過AJAX請求發送表單提交。
  • 通過CSS隱藏的表單。

使用SSL不能阻止CSRF攻擊,惡意網站可以發送https://請求。

針對GET請求站點的攻擊,可以使用Image元素來執行(這種形式的攻擊在允許圖片的論壇網站上很常見)。使用GET請求更改應用程序狀態更容易受到惡意攻擊。

因為瀏覽器將所有相關的Cookie發送到目標網站,所以可以針對使用Cookie進行身份驗證的網站進行CSRF攻擊。然而,CSRF攻擊並不僅限於利用Cookie,例如,Basic和Digest身份驗證也很脆弱。用戶使用Basic或Digest身份驗證登錄后,瀏覽器將自動發送憑據,直到會話(Session)結束。

注意:在這本文中,Session是指用戶進行身份驗證的客戶端會話。它與服務器端會話或Session中間件無關。

用戶可以通過以下方式防范CSRF漏洞:

  • 網站使用完畢后,注銷會話。
  • 定期清理瀏覽器的Cookie。

然而,CSRF漏洞根本上是Web應用程序的問題,而不是依靠用戶來解決。

ASP.NET Core MVC是如何處理CSRF的?

警告:
ASP.NET Core使用 ASP.NET Core data protection stack 來實現防請求偽造。如果在服務器集群中必配置 ASP.NET Core Data Protection,有關詳細信息,請參閱 Configuring data protection

在ASP.NET Core MVC 2.0中,FormTagHelper為HTML表單元素注入防偽造令牌。例如,Razor文件中的以下標記將自動生成防偽令牌:

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

在以下情況為HTML格式元素自動生成防偽令牌:

  • form標簽包含method="post"屬性
    • action屬性為空( action="") 或者
    • 未提供action屬性(<form method="post">)。

您可以通過以下方式禁用自動生成HTML表單元素的防偽令牌:

  • 明確禁止asp-antiforgery,例如
        <form method="post" asp-antiforgery="false">
        </form>
  • 通過使用標簽幫助器! 禁用語法,從標簽幫助器轉化為表單元素。
        <!form method="post">
        </!form>
  • 在視圖中移除FormTagHelper,您可以在Razor視圖中添加以下指令移除FormTagHelper
 @removeTagHelper Microsoft.AspNetCore.Mvc.TagHelpers.FormTagHelper, Microsoft.AspNetCore.Mvc.TagHelpers

提示:
Razor頁面會自動受到XSRF/CSRF的保護。您不必編寫任何其他代碼,有關詳細信息,請參閱XSRF/CSRF和Razor頁面

防御CSRF攻擊的最常見方法是令牌同步模式(STP)。STP是當用戶請求表單數據頁面時使用的技術。服務器將與當前用戶的標識相關聯的令牌發送給客戶端。客戶端將令牌發回服務器進行驗證。如果服務器接收到與驗證用戶身份不匹配的令牌,則該請求將被拒絕。令牌是唯一的,並且是不可預測的。令牌也可用於確保一系列請求的正確順序(確保頁面1在第2頁之前,頁面2在第3頁之前)。ASP.NET Core MVC模板中的所有表單都會生成防偽令牌,以下兩個示例演示在視圖邏輯中生成防偽令牌:

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

        </form>

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

您可以在不使用HTML標簽助手的情況下,向<form>元素顯式添加防偽令牌@Html.AntiForgeryToken

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

在前面的例子中,ASP.NET Core將添加一個隱藏的表單字段,類似於以下內容:

        <input name="__RequestVerificationToken" type="hidden" value="CfDJ8NrAkSldwD9CpLRyOtm6FiJB1Jr_F3FQJQDvhlHoLNJJrLA6zaMUmhjMsisu2D2tFkAiYgyWQawJk9vNm36sYP1esHOtamBEPvSk1_x--Sg8Ey2a-d9CV2zHVWIN9MVhvKHOSyKqdZFlYDVd69XYx-rOWPw3ilHGLN6K0Km-1p83jZzF0E4WU5OGg5ns2-m9Yw" />

ASP.NET Core 包括三個過濾器用於防偽令牌的運行:ValidateAntiForgeryTokenAutoValidateAntiforgeryTokenIgnoreAntiforgeryToken

ValidateAntiForgeryToken

ValidateAntiForgeryToken是一個可應用於單個Action、控制器或全局的操作過濾器。請求必須包含一個有效的令牌,否則對具有該過濾器Action的請求將被阻止。

        [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特性標記的Action方法需要一個令牌,包括HTTP GET請求。如果您全局使用,您可以使用IgnoreAntiforgeryToken特性來覆蓋它。

AutoValidateAntiforgeryToken

ASP.NET Core應用程序通常不會為HTTP安全方式(GET,HEAD,OPTIONS和TRACE)生成防偽令牌,而不是在全局范圍內使用ValidateAntiForgeryToken特性,然后用IgnoreAntiforgeryToken特性覆蓋它,您可以使用AutoValidateAntiforgeryToken特性。該特性與ValidateAntiForgeryToken特性相似,但對以下HTTP請求方式不需要請求令牌:

  • GET
  • HEAD
  • OPTIONS
  • TRACE

我們建議您在非API場景中廣泛使用AutoValidateAntiforgeryToken。這確保您的POST Action 默認受保護。另一種方式是在默認情況下忽略反偽造令牌,除非在個別Action方法標記了ValidateAntiForgeryToken特性,不過在這種情況下,POST Action方法有可能不受保護,使您的應用程序容易受到CSRF攻擊。即使匿名的POST請求也應該發送防偽令牌。

注意:API沒有自動機制來發送非Cookie的令牌;您的實現可能取決於您的客戶端代碼的實現。

一些例子如下所示。

示例(控制器級別):

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

示例(全局)

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

IgnoreAntiforgeryToken

IgnoreAntiforgeryToken過濾器用於取消已經使用防偽標記的Action(或控制器)的需求。應用時,此過濾器將覆蓋在更高級別(全局或控制器)上指定的過濾器ValidateAntiForgeryToken和/或AutoValidateAntiforgeryToken過濾器。

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

JavaScript,AJAX和SPA(單頁應用程序)

在傳統基於HTML的應用程序中,使用隱藏的表單字段將防偽令牌發送到服務器。在當前基於JavaScript的應用程序和單頁應用程序(SPA)中,許多請求以編程方式進行。這些AJAX請求可能會使用其它技術(如請求頭或Cookie)來發送令牌。如果使用Cookie來存儲身份驗證令牌,並在服務器上驗證API請求,那么CSRF將是一個潛在的問題,但是,如果使用本地存儲來存儲令牌,那么CSRF漏洞可能會被減輕,因為本地存儲的值不會在每個請求時自動發送到服務器。因此,使用本地存儲將反偽造令牌存儲在客戶機上,並將令牌作為請求頭發送,這是一種推薦的方式。

AngularJS

AngularJS通過約定來解決CSRF。如果服務器發送帶有名稱為XSRF-TOKEN的Cookie ,則Angular的$http服務將向該服務器發送的請求將該Cookie的值添加到請求頭。這個過程是自動的,您不需要明確設置請求頭。請求頭的名稱是X-XSRF-TOKEN,服務器會檢測該請求頭並驗證其內容。

對於ASP.NET Core API,使用此約定:

  • 配置您的應用程序,在一個Cookie中提供一個稱為XSRF-TOKEN的令牌;
  • 配置防偽服務查找名為X-XSRF-TOKEN的請求頭。
            services.AddAntiforgery(options => options.HeaderName = "X-XSRF-TOKEN");

查看示例

JavaScript

在視圖中使用JavaScript,您可以在視圖中使用服務創建令牌,您將Microsoft.AspNetCore.Antiforgery.IAntiforgery服務注入視圖並調用GetAndStoreTokens,如下所示:

@{
    ViewData["Title"] = "AJAX Demo";
}
@inject Microsoft.AspNetCore.Antiforgery.IAntiforgery Xsrf
@functions{
    public string GetAntiXsrfRequestToken()
    {
        return Xsrf.GetAndStoreTokens(Context).RequestToken;
    }
}
<h2>@ViewData["Title"].</h2>
<h3>@ViewData["Message"]</h3>

<div class="row">
    <input type="button" id="antiforgery" value="Antiforgery" />
    <script src="https://ajax.aspnetcdn.com/ajax/jquery/jquery-2.1.4.min.js"></script>
    <script>
                $("#antiforgery").click(function () {
                    $.ajax({
                        type: "post",
                        dataType: "html",
                        headers:
                        {
                            "RequestVerificationToken": '@GetAntiXsrfRequestToken()'
                        },
                        url: '@Url.Action("Antiforgery", "Home")',
                        success: function (result) {
                            alert(result);
                        },
                        error: function (err, scnd) {
                            alert(err.statusText);
                        }
                    });
                });
    </script>
</div>

這種方法無需在服務器設置Cookie或從客戶端讀取Cookie。

JavaScript還可以訪問Cookie中提供的令牌,然后使用Cookie的內容創建帶有令牌值的請求頭,如下所示。

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

然后,假設您構建的腳本發送的請求,將令牌發送為一個名為X-CSRF-TOKEN的請求頭中,請配置防偽服務以查找X-CSRF-TOKEN請求頭:

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

以下示例使用jQuery來創建一個包含相應請求頭AJAX請求:

var csrfToken = $.cookie("CSRF-TOKEN");

$.ajax({
    url: "/api/password/changepassword",
    contentType: "application/json",
    data: JSON.stringify({ "newPassword": "ReallySecurePassword999$$$" }),
    type: "POST",
    headers: {
        "X-CSRF-TOKEN": csrfToken
    }
});

配置防偽

IAntiforgery提供API來配置防偽系統。它可以在Startup類的Configure方法中使用。以下示例在應用程序的主頁生成防偽令牌,並將其作為Cookie發送到響應中(使用上述默認命名約定):

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

                return next(context);
            });
            
        }

選項

您可以在ConfigureServices方法中定制防偽選項

        services.AddAntiforgery(options => 
        {
            options.CookieDomain = "mydomain.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;
        });
選項 描述
CookieDomain Cookie的域名。默認為null
CookieName Cookie的名稱。如果未設置,系統將生成一個以DefaultCookiePrefix(".AspNetCore.Antiforgery")開頭的唯一名稱。
CookiePath Cookie設置的路徑。
FormFieldName 在視圖中隱藏表單字段的名稱。
HeaderName 防偽系統使用的請求頭的名稱。如果null,系統將僅使用表單數據。
RequireSsl 指定防偽系統是否需要SSL。默認為false。如果為true,非SSL請求會失敗。
SuppressXFrameOptionsHeader 指定是否禁止X-Frame-Options響應頭的生成。默認情況下,響應頭生成的值為“SAMEORIGIN”。默認為false

有關詳細信息,請參閱 https://docs.microsoft.com/aspnet/core/api/microsoft.aspnetcore.builder.cookieauthenticationoptions

擴展防偽

IAntiForgeryAdditionalDataProvider類型允許開發者擴展anti-XSRF系統的行為,在每個令牌中的增加額外數據。每次創建令牌時會調用GetAdditionalData方法,並且返回的值被嵌入生成的令牌內。實現者可以返回時間戳、隨機數或任何其它值,然后在驗證令牌時調用ValidateAdditionalData來驗證此數據。客戶的用戶名已經嵌入到生成的令牌中,因此不需要包含此信息。如果令牌包含補充數據但沒有配置IAntiForgeryAdditionalDataProvider,則補充數據不被驗證。

常用場景

CSRF攻擊依賴於瀏覽器默認行為,向站點發出請求同時,會發送與站點相關聯的Cookie。這些Cookies存儲在瀏覽器中,它們經常用於經過身份驗證的用戶提供會話Cookie。基於Cookie的身份驗證是一種非常流行的身份驗證模式。基於令牌的認證系統越來越受歡迎,特別是對於SPA和其它“智能客戶端”場景。

基於Cookie的身份驗證

一旦用戶使用他們的用戶名和密碼進行身份驗證,就會發出一個令牌,用於標識它們並驗證它們是否經過身份驗證。令牌存儲為Cookie,客戶端所做的每個請求都會附帶令牌。生成和驗證此Cookie是由Cookie身份驗證中間件完成的。ASP.NET Core提供了將用戶主體序列化為加密Cookie的Cookie 中間件,然后在隨后的請求中驗證Cookie,重新創建主體並將其分配給HttpContextUser屬性。

當使用Cookie時,身份驗證Cookie只是表單身份驗證憑證的一個容器。在每個請求中,票據作為的表單認證Cookie的值傳遞並通過表單身份驗證,在服務端,以標識經過身份驗證的用戶。

當用戶登錄到系統時,會在服務器端創建用戶會話,並將其存儲在數據庫或其他持久存儲中,系統生成指向數據存儲中的會話密鑰,並將其作為客戶端Cookie發送。每當用戶請求需要授權的資源時,Web服務器將檢查此會話密鑰,系統檢查關聯的用戶會話是否具有訪問請求的資源的權限。如果是,請求繼續;否則,請求返回為未授權。在這種方法下,Cookie的使用,使應用程序看起來是有狀態的,因為它能夠“記住”用戶以前已經在服務端完成了身份驗證。

用戶令牌

基於令牌的身份驗證不會在服務器上存儲會話。相反,當用戶登錄時,將頒發令牌(不是防偽令牌)。該令牌保存驗證令牌所需的所有數據,它還包含用戶信息,以claims的形式。當用戶想要訪問需要身份驗證的服務器資源時,會使用 Bearer {token} 形式的附加授權頭發送令牌給服務器。這使得應用程序無狀態,因為在每個后續請求中,令牌在請求中傳遞給服務器端驗證。該令牌未 加密 , 而是 編碼 。在服務器端,令牌可以被解碼以訪問令牌內的原始信息。要在隨后的請求中發送令牌,您可以將其存儲在瀏覽器的本地存儲或Cookie中。如果您的令牌存儲在本地存儲中,則不必擔心XSRF漏洞,但如果令牌存儲在Cookie中,則會出現問題。

多個應用程序托管在一個域中

即使example1.cloudapp.netexample2.cloudapp.net是不同的主機,在.cloudapp.net域內的所有主機之間存在一種隱式信任關系。這種隱式信任關系允許潛在的不受信任的主機影響彼此的Cookie(管理AJAX請求的同源策略不一定適用於HTTP Cookie)。ASP.NET Core運行時提供了一些緩解,用戶名被嵌入到字段令牌中,因此即使惡意子域能夠覆蓋會話令牌,它將無法為用戶生成有效的字段令牌。然而,當托管在這樣的環境中,內置的反XSRF例程仍然無法防止會話劫持或登錄CSRF攻擊。共享主機環境對會話劫持、登錄CSRF和其它攻擊都是不可控制的。

其他資源

原文:《Preventing Cross-Site Request Forgery (XSRF/CSRF) Attacks in ASP.NET Core》https://docs.microsoft.com/en-us/aspnet/core/security/anti-request-forgery
翻譯:Sweet Tang
本文地址:http://www.cnblogs.com/tdfblog/p/aspnet-core-security-anti-request-forgery.html
歡迎轉載,請在明顯位置給出出處及鏈接。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM