跨站請求偽造(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的驗證流程:
生成驗證值:
當在View中呼叫
AntiForgeryToken
時會先生成一個cookie的名稱,該名稱的字首為"_RequestVerificationToken"。如果當前的請求中已有該cookie,則直接對該cookie的值進行序列化(包含加密解密操作)得到AntiForgeryData物件。如果沒有該cookie則生成一個AntiForgeryData物件。
建立cookie,名稱為1中生成的值,值為2中AntiForgeryData物件解析後的字串(前後值會不一樣,因為還有其他的隨機值作為解析引數)。
@Html.AntiForgeryToken
會生成一個名為"_RequestVerificationToken"的隱藏控制元件,該控制元件的值是根據現有的AntiForgeryData
物件再建立一個新的AntiForgeryData
物件,序列化的字串。
匹配驗證值:
在form提交時,服務端會先根據之前生成cookie的邏輯再次獲取到cookie名稱,如果沒有找到對應cookie就丟擲異常,如果找到該cookie,就解析解密為AntiForgeryToken物件。
然後在表單中查詢名稱為"_RequestVerificationToken"的控制元件,沒有找到丟擲異常,找到的話該控制元件的值解析解密為AntiForgeryToken物件.
隱藏控制元件中的
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來實現。
參考: