參考:
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()'; }]);