在 AJAX 裡使用 AntiForgeryToken - (ASP.NET MVC 防範 CSRF 攻擊)
在MVC的架構底下,要防止CSRF 攻擊可以在檢視頁面上加入使用 AntiFrogeryToken,並且在後端所對應的 Action 方法加上Attribute [ValidateAntiForgeryToken]
作法很簡單,但是如果我今天想要使用Ajax去跟後端互動,該如何使用AntiFrogeryToken
在網路上參考 MRKT大的這篇文章,以下把內容實作出來並紀錄一下。
1.在專案根目錄下建立「App_Code」目錄,並且新增 CommonRazorFunctions.cshtml 檢視檔案
2.在 CommonRazorFunctions.cshtml 裡加入以下的 Razor @functions
透過AntiForgery.GetTokens產生兩個token並組合再一起。
3.在HomeController底下加入Action以及相對應的View
HomeController:
/// <summary>
/// 第一次Get的時候呼叫
/// </summary>
/// <returns></returns>
public ActionResult CheckAccount()
{
return View();
}
/// <summary>
/// 透過畫面上的Submit button 觸發Submit事件呼叫
/// </summary>
/// <param name="collection"></param>
/// <returns></returns>
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult CheckAccount(FormCollection collection)
{
TempData["lastName"] = collection["txtAccount"];
return View();
}
/// <summary>
/// 透過AJAX呼叫對應的Action,可看到這個Action 加了Attribute:AjaxValidateAntiForgeryToken
/// 檢查輸入的LastName是否有重複存在Table:Employees 內
/// </summary>
/// <param name="strLastName"></param>
/// <returns></returns>
[HttpPost]
[AjaxValidateAntiForgeryToken]
public ActionResult CheckAcountByAjax(string strLastName)
{
bool Exist = (from p in db.Employees.AsQueryable() where p.LastName == strLastName select p).Any();
return Json(Exist);
}
View:
View的部分我做了兩顆按鈕,一個是測試ajax用,一個是測試Submit用,
我希望達到的效果是Ajax 可以使用 AntiFrogeryToken,也不影響原有的submit button的機制
Scripts 的部分,主要是要加入headers
<script type="text/javascript">
$(function () {
$('#btnCheck').on('click', function () {
var _Object = {
strLastName: $.trim($('#txtAccount').val())
};
$.ajax({
type: "POST",
url: '@Url.Action("CheckAcountByAjax", "Home")',
data: JSON.stringify(_Object),
async: false,
contentType: "application/json; charset=utf-8",
dataType: "json",
headers:{
//加入 Request Header,這裡就會使用到在一開始所建立的 GetAntiForgeryToken()
'RequestVerificationToken':'@CommonRazorFunctions.GetAntiForgery()'
},
success: function (response) {
if(response){
alert('LastName is Exist');
}
else{
alert('LastName not Exist');
}
},
error: function (error) {
alert('error: ' + error);
}
});
});
});
</script>
後端對應的Action:CheckAcountByAjax 已經加上[AjaxValidateAntiForgeryToken]
來看一下這一段要怎麼寫:
在網站專案根目錄下的 Filters 目錄裡新增「AjaxValidateAntiForgeryTokenAttribute.cs」
裡面的code:
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
public class AjaxValidateAntiForgeryTokenAttribute : FilterAttribute, IAuthorizationFilter
{
public void OnAuthorization(AuthorizationContext filterContext)
{
try
{
//只有Ajax 才處理
if (filterContext.HttpContext.Request.IsAjaxRequest())
{
ValidateRequestHeader(filterContext.HttpContext.Request);
}
else
{
filterContext.HttpContext.Response.StatusCode = 404;
filterContext.Result = new HttpNotFoundResult();
}
}
catch (HttpAntiForgeryException e)
{
throw new HttpAntiForgeryException("Anti forgery token cookie not found");
}
}
/// <summary>
/// 解析前端丟過來的Token 是否正確
/// </summary>
/// <param name="request"></param>
private void ValidateRequestHeader(HttpRequestBase request)
{
String cookieToken = String.Empty;
String formToken = String.Empty;
String TokenValue = request.Headers["RequestVerificationToken"];
if (!String.IsNullOrWhiteSpace(TokenValue))
{
String[] Tokens = TokenValue.Split(':');
if (Tokens.Length == 2)
{
cookieToken = Tokens[0];
formToken = Tokens[1];
}
AntiForgery.Validate(cookieToken, formToken);
}
}
}
通通完成之後,將專案On起來,打開F12,可看到由 Razor @funtions「GetAntiForgeryToken()」所產生的 Token
測試Ajax的Click事件時,可以看到 Request Header的內容
在後端的程式碼下中斷點,可以看到取得的Token,
將Token分為cookieToken 與 formToken 後再使用 AntiForgery.Validate() 驗證前端所送過來的 Token 是否正確。
以上就是如何在 jQuery的 AJAX 事件內裡使用 AntiForgeryToken \。
PS:根據MRKT大的文章:如果將頁面上的 Javascript 程式抽出為 JS 檔案,則上面的作法就不適用了,在此特別說明。
在找時間測試一下。