在ASP.NET Web API中防止跨站點請求僞造(CSRF)攻擊

定義

跨站點請求僞造(CSRF)是一種惡意站點向用戶當前登錄的易受攻擊的站點發送請求的攻擊方式。

以下是CSRF攻擊的示例:

  1. 用戶使用表單驗證登錄 www.example.com。
  2. 服務器驗證用戶。服務器的響應包括一個認證cookie。
  3. 用戶沒有註銷,然後訪問了惡意網站。此惡意網站包含以下HTML表單:

    1 <h1>You Are a Winner!</h1>
    2   <form action="http://example.com/api/account" method="post">
    3     <input type="hidden" name="Transaction" value="withdraw" />
    4     <input type="hidden" name="Amount" value="1000000" />
    5   <input type="submit" value="Click Me"/>
    6 </form>

     

    請注意,表單的Action是Post到易受攻擊的網站,而不是惡意網站。這是CSRF的“跨站點”部分。

  4. 用戶點擊提交按鈕。瀏覽器請求包含身份驗證cookie。
  5. 請求在具有用戶身份驗證上下文的服務器上運行,並且可以做任何經過身份驗證的用戶才能被允許做的操作。

雖然此示例需要用戶主動單擊表單按鈕,但是惡意頁面可以輕鬆運行自動提交表單的腳本。此外,使用SSL不能阻止CSRF攻擊,因爲惡意站點也可以發送“https://”請求。

通常,針對使用Cookie進行身份驗證的網站可能會發生CSRF攻擊,因爲瀏覽器會將所有相關的Cookie發送到目標網站。然而,CSRF攻擊並不僅限於利用Cookie。例如,BasicDigest身份驗證也很脆弱。用戶使用“Basic”或“Digest”身份驗證登錄後,瀏覽器會自動發送憑證直到會話結束。

 

防僞令牌(Anti-Forgery Tokens)

爲了幫助防止CSRF攻擊,ASP.NET MVC使用防僞令牌(Anti-Forgery Tokens),也稱爲請求驗證令牌

  1. 客戶端請求包含表單的HTML頁面。
  2. 服務器在響應中包含兩個令牌。一個令牌作爲一個Cookie發送。另一個放置在隱藏的表單字段中。令牌隨機生成,使攻擊者無法猜測令牌值。
  3. 當客戶端提交表單時,它必須將兩個令牌發送回服務器。客戶端將Cookie令牌作爲cookie發送,並在表單數據中發送表單令牌。(瀏覽器客戶端在用戶提交表單時自動執行此操作。)
  4. 如果請求不包含這兩個令牌,則服務器不允許該請求。

以下是具有隱藏表單令牌的HTML表單的示例:

<form action="/Home/Test" method="post">
    <input name="__RequestVerificationToken" type="hidden"   
           value="6fGBtLZmVBZ59oUad1Fr33BuPxANKY9q3Srr5y[...]" />    
    <input type="submit" value="Submit" />
</form>

防僞令牌的工作原理是惡意頁面無法讀取用戶的令牌,因爲有同源策略。(同源策略可以防止兩個不同站點上託管的文件訪問對方的內容,所以在前面的例子中,惡意頁面可以發送請求到example.com,但不能讀取響應。)

爲了防止CSRF攻擊,任何在用戶登錄後瀏覽器默認發送憑據的身份驗證協議都應使用防僞令牌。包括基於cookie的身份驗證協議,如表單身份驗證以及BasicDigest(摘要)身份驗證等協議。

您應該對任何非安全方法(POST,PUT,DELETE)要求防僞令牌。另外,確保安全的方法(GET,HEAD)沒有任何副作用。此外,如果您啓用跨域支持(如CORS或JSONP),則甚至安全的方法(如GET)也可能容易受到CSRF攻擊的攻擊,從而允許攻擊者讀取潛在的敏感數據。

 

ASP.NET MVC中的防僞令牌

要將防僞令牌添加到Razor頁面,請使用HtmlHelper.AntiForgeryToken幫助方法:

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

此方法添加隱藏表單字段,並設置cookie令牌。

 

Anti-CSRF 和 AJAX

表單令牌可能是AJAX請求的一個問題,因爲AJAX請求可能會發送JSON數據,而不是HTML表單數據。一個解決方案是將Token發送到自定義HTTP標頭。以下代碼使用Razor語法生成令牌,然後將令牌添加到AJAX請求。令牌通過調用AntiForgery.GetTokens在服務器端生成。

<script>
    @functions{
        public string TokenHeaderValue()
        {
            string cookieToken, formToken;
            AntiForgery.GetTokens(null, out cookieToken, out formToken);
            return cookieToken + ":" + formToken;                
        }
    }

    $.ajax("api/values", {
        type: "post",
        contentType: "application/json",
        data: {  }, // JSON data goes here
        dataType: "json",
        headers: {
            'RequestVerificationToken': '@TokenHeaderValue()'
        }
    });
</script>

 

處理請求時,從請求頭中提取令牌。然後調用AntiForgery.Validate方法來驗證令牌。如果令牌無效,該驗證方法將引發異常。

void ValidateRequestHeader(HttpRequestMessage request)
{
    string cookieToken = "";
    string formToken = "";

    IEnumerable<string> tokenHeaders;
    if (request.Headers.TryGetValues("RequestVerificationToken", out tokenHeaders))
    {
        string[] tokens = tokenHeaders.First().Split(':');
        if (tokens.Length == 2)
        {
            cookieToken = tokens[0].Trim();
            formToken = tokens[1].Trim();
        }
    }
    AntiForgery.Validate(cookieToken, formToken);
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章