asp.net MVC中防止跨站请求攻击(CSRF)的ajax用法
参考:
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()'; }]);