跨站請求偽造(CSRF)-簡述

JoeSnail發表於2017-10-27

跨站請求偽造(CSRF)-簡述

跨站請求偽造(英語:Cross-site request forgery),也被稱為 one-click attack 或者 session riding,通常縮寫為 或者 XSRF, 是一種挾制使用者在當前已登入的Web應用程式上執行非本意的操作的攻擊方法。[1] 跟跨網站指令碼(XSS)相比,XSS 利用的是使用者對指定網站的信任,CSRF 利用的是網站對使用者網頁瀏覽器的信任。(維基百科)

當我們請求一個目標網站時,瀏覽器會傳送該目標網站相關的cookie資訊。當我們瀏覽其他的網站時,如果我們沒有退出該目標網站,而其他的網站有一個向目標網站的操作連結,這時因為我們線上,且有cookie資訊,那麼目標網站就會認為這是一個合法的請求而執行。但是這個操作不是我們自己請求的,而是惡意者用我們自己被認證過的身份執行的操作。

這種惡意的網址並不需要一個特定的網站,它可以藏身在任何一個由使用者生成內容的網站中,如論壇,部落格等。

如果有賬戶名為Alice的使用者訪問了惡意站點,而她之前剛訪問過線上交易網站,登入資訊尚未過期,那麼她就會損失1000資金。

Get方式攻擊:

假設一個線上交易網站有一個轉賬的url:

http://www.examplebank.com/withdraw?account=AccoutName&amount=1000&for=PayeeName

一個惡意者在另一個網站新增了如下程式碼

<img src="http://www.examplebank.com/withdraw?account=Alice&amount=1000&for=Badman">

Post方式攻擊:

//目標網站的form
<form id="form" action="withdraw/trans" method="post">
    <input name="account" type="text"/>
    <input name="amount" type="text"/>
    <input name="forAccount" type="text"/>
</form>

惡意連結

//指向惡意Html的連結
<img src="http://other/maliciouspage.html"/>

惡意html:

<html>
 <head>
       <script type="text/javascript">
         window.onload = function () {
             document.myform.submit(); 
         }
         </script>
 </head>
 <body>
    <form id="myform" name="myform" action="withdraw/trans" method="post"/>
        <input name="account" type="hidden" value="Alice" />
        <input name="amount" type="hidden" value="1000"/>
        <input name="forAccount" type="hidden" value="Badman"/>
    </form>
</body>
</html>

惡意Html部署在http://other/maliciouspage.html。惡意連結在任何一個可以由使用者生成內容的網站,當有使用者是Alice瀏覽到一個有惡意連結的網站時,就會載入惡意html,執行form方法。


@Html.AntiForgeryToken

如何避免這個惡意請求呢?

既然瀏覽器是不可信的,那麼就用一個非瀏覽器的而是跟頁面自身(使用者唯一)才有的唯一隨機值作為驗證物件。

@Html.AntiForgeryToken的驗證流程:

生成驗證值:

  1. 當在View中呼叫AntiForgeryToken時會先生成一個cookie的名稱,該名稱的字首為"_RequestVerificationToken"。

  2. 如果當前的請求中已有該cookie,則直接對該cookie的值進行序列化(包含加密解密操作)得到AntiForgeryData物件。如果沒有該cookie則生成一個AntiForgeryData物件。

  3. 建立cookie,名稱為1中生成的值,值為2中AntiForgeryData物件解析後的字串(前後值會不一樣,因為還有其他的隨機值作為解析引數)。

  4. @Html.AntiForgeryToken會生成一個名為"_RequestVerificationToken"的隱藏控制元件,該控制元件的值是根據現有的AntiForgeryData物件再建立一個新的AntiForgeryData物件,序列化的字串。

匹配驗證值:

  1. 在form提交時,服務端會先根據之前生成cookie的邏輯再次獲取到cookie名稱,如果沒有找到對應cookie就丟擲異常,如果找到該cookie,就解析解密為AntiForgeryToken物件。

  2. 然後在表單中查詢名稱為"_RequestVerificationToken"的控制元件,沒有找到丟擲異常,找到的話該控制元件的值解析解密為AntiForgeryToken物件.

  3. 隱藏控制元件中的AntiForgeryToken和cookie解析解密的AntiForgeryToken物件匹配,一致為合法,不一致為非法。

因為@Html.AntiForgeryToken的值是隨機且加密的,所以惡意html的表單裡很難獲取到正確的值。

Cookie:

    HTTP/1.1 200 OK
    Cache-Control: private
    Set-Cookie: __RequestVerificationToken_L012Y0FwcDEx=EYPOofprbB0og8vI+Pzr1unY0Ye5BihYJgoIYBqzvZDZ+hcT5QUu+fj2hvFUVTTCFAZdjgCPzxwIGsoNdEyD8nSUbgapk8Xp3+ZD8cxguUrgl0lAdFd4ZGWEYzz0IN58l5saPJpuaChVR4QaMNbilNG4y7xiN2/UCrBF80LmPO4=; path=/; HttpOnly

Form:

 <form action="..." method="post">
<input name="__RequestVerificationToken" type="hidden" value="yvLaFQ81JVgguKECyF/oQ+pc2/6q0MuLEaF73PvY7pvxaE68lO5qgXZWhfqIk721CBS0SJZjvOjbc7o7GL3SQ3RxIW90no7FcxzR6ohHUYEKdxyfTBuAVjAuoil5miwoY8+6HNoSPbztyhMVvtCsQDtvQfyW1GNa7qvlQSqYxQW7b6nAR2W0OxNi4NgrFEqbMFrD+4CwwAg4PUWpvcQxYA==" />
</form>

@Html.AntiForgeryToken的使用:

//Razor:
<form id="form" action="withdraw/trans" method="post">
   @Html.AntiForgeryToken()
    <input name="account" type="text"/>
    <input name="amount" type="text"/>
    <input name="forAccount" type="text"/>
</form>

//Controller:新增特性[ValidateAntiForgeryToken]且必須是HttpPost
[HttpPost]
[ValidateAntiForgeryToken]
public ActinResult Trans(string account,double amount,string forAccount)
{
    return View();
}

get請求最好是隻讀的,對於有操作的請求最好用post來實現。


參考:

相關文章