AntiForgeryToken CSRF XSRF

實現與防範CSRF跨網站請求偽造攻擊

【Roger】洪承孝(昕力DTD) 2025/12/23 10:45:56
25

 

相依性參考

using Microsoft.AspNetCore.Antiforgery

「Antiforgery」專屬中介(IMiddleware)

    首先我們要先創建一個繼承「IMiddleware」的類別,並在「InvokeAsync」方法中撰寫
「ValidateRequestAsync」驗證方法
using Microsoft.AspNetCore.Antiforgery;

namespace ProtectCSRFSample.Filters
{
    /// <summary>
    /// Antiforgery中介
    /// </summary>
    public class AntiforgeryMiddleware : IMiddleware
    {
        //宣告本地Antiforgery介面
        private readonly IAntiforgery _antiforgery;

        public async Task InvokeAsync(HttpContext context, RequestDelegate next)
        {
            //比對字串,找出Client是否為Get發送
            var isGetRequest = string.Equals("GET", context.Request.Method, StringComparison.OrdinalIgnoreCase);
            //若是非Get發送,則驗證Request token
            if (!isGetRequest)
            {
                _antiforgery.ValidateRequestAsync(context).GetAwaiter().GetResult();
            }

            //交由下一層接續處理
            await next(context);
        }

        /// <summary>
        /// 初始化
        /// </summary>
        public AntiforgeryMiddleware(IAntiforgery antiforgery)
        {
            this._antiforgery = antiforgery;
        }
    }
}
 

註冊服務

在「Program.cs」中註冊「Antiforgery」服務並定義一組標頭來與前端Token做匹配
//註冊Antiforgery服務
builder.Services.AddAntiforgery(options =>
{
    //定義標頭
    options.HeaderName = "X-XSRF-TOKEN";
}).AddScoped<AntiforgeryMiddleware>();
 

啟用服務

在「Program.cs」中啟用「Antiforgery」服務
//啟用Antiforgery服務
app.UseMiddleware<AntiforgeryMiddleware>();

 

定義後端權杖屬性

    這裡主要用來定義「IActionResult」或「Controller」上的權杖屬性,
這個設定會決定「Antiforgery」這個服務是否會介入驗證
給予Controller具備AutoValidateAntiforgeryToken的屬性
[AutoValidateAntiforgeryToken]
public class HomeController : Controller
{
    private readonly ILogger<HomeController> _logger;
    private readonly IAntiforgery _antiforgery;

給予Action具備IgnoreAntiforgeryToken的屬性
/// <summary>
/// 付款
/// </summary>
/// <returns></returns>
//[IgnoreAntiforgeryToken]
[Route("Payment")]
[HttpPost]
public IActionResult Payment()
{
    //從用戶現金扣款
    _Model.Personal_Cash -= 100;
    return Json(_Model.Personal_Cash);
}
  1. ValidateAntiForgeryToken:指定套用此屬性的類別或方法會驗證防偽造權杖。(如果無法使用防偽造權杖,或權杖無效,驗證將會失敗,而且動作方法不會執行)
  2. IgnoreAntiforgeryToken:略過反Forgery權杖驗證的篩選準則。
  3. AutoValidateAntiforgeryToken:屬性,會針對所有不安全的HTTP方法,對反分叉權杖進行驗證。(GET、HEAD、OPTIONS和TRACE以外的HTTP方法需要反移轉權杖)

定義後端取得Token方法,同時設定Response Cookies

    我們在這裡用「_antiforgery.GetAndStoreTokens」來取得Tokens,
接著將「Tokens.RequestToken」定義成「RequestToken」並將此Tokens設定給「XSRF-TOKEN」,
這樣前端就可以透過Cookies取得該Tokens
[Route("{action}")]
[HttpGet]
public IActionResult GetToken()
{
    //建立並設定cookie token到cookie上
    var Tokens = _antiforgery.GetAndStoreTokens(this.HttpContext);
    //取得Request token(這裡與cookie token不同)
    var RequestToken = Tokens.RequestToken;
    if (RequestToken == null)
        return Error();

    this.HttpContext.Response.Cookies.Append("XSRF-TOKEN", RequestToken, new CookieOptions
    { HttpOnly = false });

    return Ok();
}

定義前端Request Header標頭

    接著回到前端,我們要將資料發送到後端以前要先取得先前於後端預先產生的「Tokens」,
於是可以透過「document.cookie」方式取得,接著以字串分割方式取得名為「XSRF-TOKEN」的Token。
    
    最後透過以下範例使用jQuery的fetch方式定義「headers」,「X-XSRF-TOKEN」為先前在「註冊服務」
的步驟中所定義的標頭,「xsrfToken」的部分則是從Cookie取得到的Token。
const xsrfToken = document.cookie.split("; ")
    .find(row => row.startsWith("XSRF-TOKEN="))
    .split("=")[1];

fetch('/Payment', {
    method: 'POST',
    headers: {
    "X-XSRF-TOKEN": xsrfToken,
    "Content-Type": "application/json",
    "Accept": "application/json"
    }
    }).then(response => {
        if (response.ok)
            return response.json();
    }).then(result => {
        $("#Lab_Cash_Val").text(result);
    });

範例練習-1

網頁基本設計與操作

    此練習的構想是付款系統,這裡用一個簡單的例子去驗證,首先我們會先設計「登入頁面」及「後台頁面」,
用戶透過登入進入到後台即可進行付款,基本操作功能如下所示:

 

模擬用戶端被攻擊效果(未加上AntiForgeryToken的防護效果)

    此為尚未加上AntiForgeryToken的攻擊展示,這裡以Postman當作攻擊端竊取用戶端Post URL後瘋狂觸發,
而當用戶端點擊「Update」功能後發現個人現金正不斷減少。

 

模擬用戶端被攻擊效果(加上AntiForgeryToken的防護效果)

    當網頁加上AntiForgeryToken防護之後,在按照先前攻擊的手法可以發現用戶端在更新頁面之後,
發現個人現金已經不會像之前一樣莫名減少,因為攻擊者不知道「Token」故達到防護效果。

 

【Roger】洪承孝(昕力DTD)