【ASP.net MVC】為你的網站多一點點的安全性(Cross-Site Request Forgery,CSRF/XSRF)
眾多網站攻擊手法之一的CSRF,一直是網站攻擊的前幾名,在OWASP每年公布的十大攻擊手法,排名也是居高不下,至於什麼是CSRF呢?以下做個簡單的說明吧
跨站請求偽造(Cross-site Request Forgery, CSRF/XSRF)對於網站應用安全一直是個嚴重的問題,即駭客利用網站對於合法使用者的信任,以合法使用者的身分向網站發出偽造請求,並在使用者不知情的情況下執行一些惡意行為,甚至執行駭客所指定的行為。此問題的關鍵在於網站會執行該偽造請求而沒有事先證實該請求是否由合法使用者自己發出。在偵測CSRF上所遭遇的問題在於HTTP協定無法辨識那些請求是屬於合法使用者自己發出。(TWISC@NTUST網路應用安全知識庫 )
所謂知己知彼,百戰百勝!先了解攻擊的手法吧!
在使用網路銀行轉帳的時候,通常會使用POST的模式送出,但駭客發現,也可以利用GET的模式送出請求,此時駭客利用這個弱點就可以將您的資產轉入自己的
眾多網站攻擊手法之一的CSRF,一直是網站攻擊的前幾名,在OWASP每年公布的十大攻擊手法,排名也是居高不下,至於什麼是CSRF呢?以下做個簡單的說明吧
跨站請求偽造(Cross-site Request Forgery, CSRF/XSRF)對於網站應用安全一直是個嚴重的問題,即駭客利用網站對於合法使用者的信任,以合法使用者的身分向網站發出偽造請求,並在使用者不知情的情況下執行一些惡意行為,甚至執行駭客所指定的行為。此問題的關鍵在於網站會執行該偽造請求而沒有事先證實該請求是否由合法使用者自己發出。在偵測CSRF上所遭遇的問題在於HTTP協定無法辨識那些請求是屬於合法使用者自己發出。(TWISC@NTUST網路應用安全知識庫 )
所謂知己知彼,百戰百勝!先了解攻擊的手法吧!
在使用網路銀行轉帳的時候,通常會使用POST的模式送出,但駭客發現,也可以利用GET的模式送出請求,此時駭客利用這個弱點就可以將您的資產轉入自己的名下
目前防禦CSRF的方法有以下三種
1.驗證Http Referer
2.在網頁中與cookie加入一個Token
3.在Http header中加入自訂義驗證
在MVC中,有個簡單的方法,可以防止CSRF的攻擊!
以下利用會員註冊來說明與實作
首先,我們在Controller中產生兩個ActionResult,在POST的過濾器而外加入一個Filter:[ValidateAntiForgeryToken]
public ActionResult Create()
{
if (User.Identity.IsAuthenticated)
return RedirectToAction("LoginText");
SelectListItem[] SexList = new SelectListItem[]{
new SelectListItem { Text ="請選擇性別",Value="",Selected=true},
new SelectListItem { Text ="我是男生",Value="true"},
new SelectListItem { Text ="我是女生",Value="false"}
};
ViewBag.Sex = SexList;
return View();
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(MemberTable CreateMember)
{
MemberServices CreateMemberServices = new MemberServices();
CreateMemberServices.Register(CreateMember);
return RedirectToAction("Create");
}
頁面顯示的部分則是需要加上一個
@Html.AntiForgeryToken()
在表單當中,這樣在我們POST的資料時,可以一併送出驗證的Token
@using (Html.BeginForm())
{
@Html.AntiForgeryToken()
@Html.ValidationSummary(true)
<fieldset>
<legend>MemberTable</legend>
<div class="editor-label">
@Html.LabelFor(model => model.Email)
</div>
<div class="editor-field">
@Html.EditorFor(model => model.Email)
@Html.ValidationMessageFor(model => model.Email)
</div>
<div class="editor-label">
@Html.LabelFor(model => model.Password)
</div>
<div class="editor-field">
@Html.EditorFor(model => model.Password)
@Html.ValidationMessageFor(model => model.Password)
</div>
<div class="editor-label">
@Html.LabelFor(model => model.ConfirmPassword)
</div>
<div class="editor-field">
@Html.EditorFor(model => model.ConfirmPassword)
@Html.ValidationMessageFor(model => model.ConfirmPassword)
</div>
<div class="editor-label">
@Html.LabelFor(model => model.RealName)
</div>
<div class="editor-field">
@Html.EditorFor(model => model.RealName)
@Html.ValidationMessageFor(model => model.RealName)
</div>
<div class="editor-label">
@Html.LabelFor(model => model.Birthday)
</div>
<div class="editor-field">
@Html.EditorFor(model => model.Birthday)
@Html.ValidationMessageFor(model => model.Birthday)
</div>
<div class="editor-label">
@Html.LabelFor(model => model.sex)
</div>
<div class="editor-field">
@Html.DropDownListFor(model => model.sex, (SelectListItem[])ViewBag.SexList)
@Html.ValidationMessageFor(model => model.sex)
</div>
<p>
<input type="submit" value="Save" />
</p>
</fieldset>
}
這樣就可以防止CSRF的偽造表單,以免你的會員系統被惡意人士不斷地發出註冊請求囉!
那假設沒有驗證的表單資訊,傳入了我們的Server會出現什麼樣的訊息呢?
來做個小實驗吧!先把在View上面的@Html.AntiForgeryToken()移除
@using (Html.BeginForm())
{
@Html.ValidationSummary(true)
<fieldset>
<legend>MemberTable</legend>
<div class="editor-label">
@Html.LabelFor(model => model.Email)
</div>
<div class="editor-field">
@Html.EditorFor(model => model.Email)
@Html.ValidationMessageFor(model => model.Email)
</div>
<div class="editor-label">
@Html.LabelFor(model => model.Password)
</div>
<div class="editor-field">
@Html.EditorFor(model => model.Password)
@Html.ValidationMessageFor(model => model.Password)
</div>
<div class="editor-label">
@Html.LabelFor(model => model.ConfirmPassword)
</div>
<div class="editor-field">
@Html.EditorFor(model => model.ConfirmPassword)
@Html.ValidationMessageFor(model => model.ConfirmPassword)
</div>
<div class="editor-label">
@Html.LabelFor(model => model.RealName)
</div>
<div class="editor-field">
@Html.EditorFor(model => model.RealName)
@Html.ValidationMessageFor(model => model.RealName)
</div>
<div class="editor-label">
@Html.LabelFor(model => model.Birthday)
</div>
<div class="editor-field">
@Html.EditorFor(model => model.Birthday)
@Html.ValidationMessageFor(model => model.Birthday)
</div>
<div class="editor-label">
@Html.LabelFor(model => model.sex)
</div>
<div class="editor-field">
@Html.DropDownListFor(model => model.sex, (SelectListItem[])ViewBag.SexList)
@Html.ValidationMessageFor(model => model.sex)
</div>
<p>
<input type="submit" value="Save" />
</p>
</fieldset>
}
然後,我們開始註冊會員,看接下來會發生什麼事情
結果出現了 "需要的反仿冒表單欄位 "__RequestVerificationToken" 不存在。"的錯誤訊息!這時候在網站上線後,出現這樣的訊息似乎不太美觀...(應該不會有人想,直接讓他可以進行註冊吧?)
在web.config中,加入一段讓我們可以自訂錯誤頁面導向設定
<customErrors mode="On" defaultRedirect="~/Error" redirectMode="ResponseRedirect"></customErrors>
之後再加入一個cshtml/vbhtml的檢視頁面,為CSRF做一個美觀的錯誤訊息吧!
設計好CSRF的錯誤頁面後,在ValidateAntiForgeryToken的Filter下方加入
[HandleError(ExceptionType=typeof(HttpAntiForgeryException),View="CSRF")]//當Token驗證失敗的時候,導向這個頁面給使用者
PS.若View只設定頁面的黨名,MVC會自動抓取Shared資料夾目錄下的檔案,若要指定其他頁面需要明確指定頁面路徑
這樣一來,當發生了CSRF的驗證失敗,就會導向我們剛剛設計過的頁面囉!
如此一來,顧及了網站的美觀,還增加了它的安全性!
真是一舉數得阿!
大家好我是饅頭,希望大家喜歡我的文章
如果有錯誤的地方請不吝指教 ^_^