ASP.NET MVC架構下如何防範表單偽造(CSRF)

首先,何謂CSRF?CSRF是Cross-Site Request Forgery的簡稱,簡單來說就是惡意駭客為竊取資訊用script創造成的假表單。詳情請參考OWASP官網解說:Cross-Site_Request_Forgery

那麼在ASP.NET MVC底下,其實要防範這個問題相當簡單,只要在Razor Ajax.BeginForm或是Html.BeginForm,甚至是一般傳統Html

內加上@Html.AntiForgeryToken,就會在表單Submit的時候產生2個相同值的Token。

第一個token會直接以hidden型態的input內嵌於form內,如下所示:


<form action="/UserProfile/SubmitUpdate" method="post">
    <input name="__RequestVerificationToken" type="hidden" value="saTFWpkKN0BYazFtN6c4YbZAmsEwG0srqlUqqloi/fVgeV2ciIFVmelvzwRZpArs" />
</form>

第二個token會以__RequestVerificationToken的Key值存在Cookie,利用開發者工具就可以看到它的存在。

最後一步就是要在你的Controller Action上掛上一個[ValidateAntiForgeryToken]的Attribute,讓前述的兩個Token會在Controller的OnAuthentication這個方法裡被驗證。如果想要在這個方法內看到Token的值,可以如下寫法,將Cookie的key-value pair寫到Log檔。

protected override void OnAuthorization(AuthorizationContext filterContext)
{
    if (filterContext.HttpContext.Request.IsAjaxRequest())
    {
    	string[] cookieKeys = filterContext.HttpContext.Request.Cookies.AllKeys;
	StringBuilder sb = new StringBuilder();
	foreach (string key in cookieKeys)
	{
		string keyValue = string.Format("\r\nKey:{0}\tValue:{1}{2}", 
			key.ToString(), 
			filterContext.HttpContext.Request.Cookies[key].Value, 
			Environment.NewLine
		);
		sb.Append(keyValue);
	}
	logger.Info(sb.ToString());
    }
}

而輸出大概如下圖:

[INFO]2017-06-16 11:08:35.1668
[INFO]
Key:ASP.NET_SessionId	
Value:u3mhsdlpde2n5qp0gaun3v5u

Key:__RequestVerificationToken_L0dDUg2	
Value:HP_VXEUkKWG9-piV5qrIyAIPWJ_Rzk8zMxnL74s5Wu2Vhn8oKQqlGhL5B0F4gWMaO7zKZxsvEQvsiQZBYBBiVcw7RJGi-xHuwo71VP67y0g1

Key:__RequestVerificationToken_L0dDRQ2	
Value:IJADVlxFaelMscsT-luOsu6-99fyg81xKsc_Dq7NrAr-X4WlxsmLi7nm6NLN_yjMO3VgBP2dxOPXMtT1UQL-A4XNL6p_Rnj-K23oyiAmFuQ1

Key:__RequestVerificationToken_L0NT0	
Value:qKVXKo7alTSBOCE3HiNZ3sRlve7rpKqhxCQMavJI9jjP-PTTgXccCMsLFm5b9mcsD7vN6rEzkYoicYSUxFQkY14ObJ2qxu8N3igZl8nqBt81

Key:.ASPXAUTH	
Value:5E749B9CE6E4AEE584AD9FD7BED6652C3BDBB5072D1E0FD927C1B7AD1A3E0E88EEE97CAC8FD91A077976D110DF6CFE8036696D6E3C950D24BAF43CA4D6BCE70E71E441FDB9A29146172B4B917046D181526CFB921D6FCCE581CB661A4D37F6FC

「原則上」問題到這裡就結束了,但有的時候如果主頁面開一個window,而那個window是一個PartialView,且其內容是會再發Ajax取得資料的話,有機會造成取不到Token而驗證失敗的狀況發生。原因可能是因為IIS尚未重啟,或是產生過舊的Token。
那麼,該如何防範呢?目前嘗試最有效的解法就是主要外層的form與內層的PartialView都加上form及@Html.AntiForgeryToken,然後在不同的層級用不同的querySelector取得前端頁面token的值,實作程式碼截取片斷如下:

主畫面:

主畫面發Ajax:

內層PartialView:

內層PartialView發Ajax:

以上,大概是目前防範CSRF的解法,大部份的狀況是發生在Internet Explorer, Google Chrome較無此問題發生,但瀏覽器內部實作並非我們所能控制的,能做的就只有調整程式碼。

p.s.若上版後仍然錯誤,重新整理網頁或重啟IIS問題就會解決了。

作者: George Chou

嗨,大家好,我是George Chou,一位半路出家的小Programmer。 對程式設計相當有興趣,尤其是Java,所以踏上了程式之路, 歡迎大家一同交流學習。

發表迴響

你的電子郵件位址並不會被公開。 必要欄位標記為 *