跨站請求僞造(英語:Cross-site request forgery),也被稱爲 one-click attack 或者 session riding,一般縮寫爲 或者 XSRF, 是一種挾制用戶在當前已登陸的Web應用程序上執行非本意的操做的攻擊方法。[1] 跟跨網站腳本(XSS)相比,XSS 利用的是用戶對指定網站的信任,CSRF 利用的是網站對用戶網頁瀏覽器的信任。(維基百科)javascript
當咱們請求一個目標網站時,瀏覽器會發送該目標網站相關的cookie信息。當咱們瀏覽其餘的網站時,若是咱們沒有退出該目標網站,而其餘的網站有一個向目標網站的操做連接,這時由於咱們在線,且有cookie信息,那麼目標網站就會認爲這是一個合法的請求而執行。可是這個操做不是咱們本身請求的,而是惡意者用咱們本身被認證過的身份執行的操做。html
這種惡意的網址並不須要一個特定的網站,它能夠藏身在任何一個由用戶生成內容的網站中,如論壇,博客等。java
若是有帳戶名爲Alice的用戶訪問了惡意站點,而她以前剛訪問過在線交易網站,登陸信息還沒有過時,那麼她就會損失1000資金。瀏覽器
假設一個在線交易網站有一個轉帳的url:cookie
http://www.examplebank.com/withdraw?account=AccoutName&amount=1000&for=PayeeName
一個惡意者在另外一個網站添加了以下代碼session
<img src="http://www.examplebank.com/withdraw?account=Alice&amount=1000&for=Badman">
//目標網站的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>
惡意連接post
//指向惡意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方法。加密
如何避免這個惡意請求呢?url
既然瀏覽器是不可信的,那麼就用一個非瀏覽器的而是跟頁面自身(用戶惟一)纔有的惟一隨機值做爲驗證對象。
生成驗證值:
當在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>
//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來實現。
參考: