參考:
Preventing Cross-Site Request Forgery (CSRF) Attacks
Validating .NET MVC 4 anti forgery tokens in ajax requests
在mvc中,微軟提供了一個簡單的方法來防止CSRF,就是在前端form表單里加上Anti-Forgery Tokens
<form action="/Home/Test" method="post">
<input name="__RequestVerificationToken" type="hidden"
value="6fGBtLZmVBZ59oUad1Fr33BuPxANKY9q3Srr5y[...]" />
<input type="submit" value="Submit" />
</form>
razor的寫法很簡單:
@using (Html.BeginForm("Manage", "Account")) {
@Html.AntiForgeryToken()
}
后端只需要在action上加上[ValidateAntiForgeryToken]標簽即可
//
// POST: /execute/uploadfile/
[HttpPost]
[MyValidateAntiForgeryToken]
public ActionResult UploadFile()
{
// codes here
}
那么ajax請求呢?
首先,我嘗試在header里面加了__RequestVerificationToken,值就從razor生成,結果報錯,最終發現還是要自定義,默認的[ValidateAntiForgeryToken]不行,所以我們就自定義一個MyValidateAntiForgeryTokenAttribute(片段1),需要注意以下幾點:
- 從filter中自己判斷請求是不是ajax請求,如果你確實發起了ajax請求,還被
Reauest.IsAjaxRequest()方法判定為false,那么檢查一下header里面有沒有{"X-Requested-With" : "XMLHttpRequest"}這個鍵和值 - 既然是自定義過濾了,那么header里面這個鍵就沒必要非得是
__RequestVerificationToken了,你可以任意自定義,只要前后對應即可。 - 至於如何取值,上面介紹了用
@html擴展的寫法,然后用js的方法把值取出來,也可以用下面片段2的寫法,直接用razor寫到js里面去,片段3中有使用方法(本例中一個angular的例子,注意甄別) - 最后,在應用的action前把系統默認的標簽改成我們自定義的即可。
片段1,自定義的anti csrf過濾器
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
public class MyValidateAntiForgeryTokenAttribute : FilterAttribute, IAuthorizationFilter
{
private void ValidateRequestHeader(HttpRequestBase request)
{
string cookieToken = String.Empty;
string formToken = String.Empty;
string tokenValue = request.Headers["RequestVerificationToken"];
if (!String.IsNullOrEmpty(tokenValue))
{
string[] tokens = tokenValue.Split(':');
if (tokens.Length == 2)
{
cookieToken = tokens[0].Trim();
formToken = tokens[1].Trim();
}
}
AntiForgery.Validate(cookieToken, formToken);
}
public void OnAuthorization(AuthorizationContext filterContext)
{
try
{
if (filterContext.HttpContext.Request.IsAjaxRequest())
{
ValidateRequestHeader(filterContext.HttpContext.Request);
}
else
{
AntiForgery.Validate();
}
}
catch (HttpAntiForgeryException e)
{
throw new HttpAntiForgeryException("Anti forgery token cookie not found");
}
}
}
片段2,定義一個@function片斷
@functions{
public string TokenHeaderValue()
{
string cookieToken, formToken;
AntiForgery.GetTokens(null, out cookieToken, out formToken);
return cookieToken + ":" + formToken;
}
}
片段3, 使用定義的@function片斷(實例為angular中為http請求添加全局的header)
var app = angular.module('srv', ['angularLocalStorage', 'angularFileUpload']);
app.config(['$httpProvider', function($httpProvider) {
$httpProvider.defaults.headers.common["X-Requested-With"] = "XMLHttpRequest";
$httpProvider.defaults.headers.common["RequestVerificationToken"] = '@TokenHeaderValue()';
}]);
