2013-04-18 95 views

回答

4

反CSRF通常通过匹配来自饼干和身体令牌来验证在非AJAX调用浏览器等形式后的请求来完成。

在Ajax调用,建议把令牌在自定义标题。如果你安装了最新的ASP.NET 2012.2 update。它在MVC项目对话框中有一个spa模板,演示如何在SPA应用程序中防止CSRF。以下是从模板复制的代码,以便从服务器端验证标头令牌。

public class ValidateHttpAntiForgeryTokenAttribute : AuthorizationFilterAttribute 
{ 
    public override void OnAuthorization(HttpActionContext actionContext) 
    { 
     HttpRequestMessage request = actionContext.ControllerContext.Request; 

     try 
     { 
      if (IsAjaxRequest(request)) 
      { 
       ValidateRequestHeader(request); 
      } 
      else 
      { 
       AntiForgery.Validate(); 
      } 
     } 
     catch (HttpAntiForgeryException e) 
     { 
      actionContext.Response = request.CreateErrorResponse(HttpStatusCode.Forbidden, e); 
     } 
    } 

    private bool IsAjaxRequest(HttpRequestMessage request) 
    { 
     IEnumerable<string> xRequestedWithHeaders; 
     if (request.Headers.TryGetValues("X-Requested-With", out xRequestedWithHeaders)) 
     { 
      string headerValue = xRequestedWithHeaders.FirstOrDefault(); 
      if (!String.IsNullOrEmpty(headerValue)) 
      { 
       return String.Equals(headerValue, "XMLHttpRequest", StringComparison.OrdinalIgnoreCase); 
      } 
     } 

     return false; 
    } 

    private void ValidateRequestHeader(HttpRequestMessage request) 
    { 
     string cookieToken = String.Empty; 
     string formToken = String.Empty; 

     IEnumerable<string> tokenHeaders; 
     if (request.Headers.TryGetValues("RequestVerificationToken", out tokenHeaders)) 
     { 
      string tokenValue = tokenHeaders.FirstOrDefault(); 
      if (!String.IsNullOrEmpty(tokenValue)) 
      { 
       string[] tokens = tokenValue.Split(':'); 
       if (tokens.Length == 2) 
       { 
        cookieToken = tokens[0].Trim(); 
        formToken = tokens[1].Trim(); 
       } 
      } 
     } 

     AntiForgery.Validate(cookieToken, formToken); 
    } 
} 

从客户端,您还需要在ajax调用中设置标头。这是todo.datacontext.js代码:

function ajaxRequest(type, url, data, dataType) { // Ajax helper 
    var options = { 
     dataType: dataType || "json", 
     contentType: "application/json", 
     cache: false, 
     type: type, 
     data: data ? data.toJson() : null 
    }; 
    var antiForgeryToken = $("#antiForgeryToken").val(); 
    if (antiForgeryToken) { 
     options.headers = { 
      'RequestVerificationToken': antiForgeryToken 
     } 
    } 
    return $.ajax(url, options); 
} 
0

我写了一个动作过滤器类来处理反伪造的令牌验证..

public class WebApiValidateJsonToken : System.Web.Http.Filters.ActionFilterAttribute 
{ 
    public override void OnActionExecuting(HttpActionContext filterContext) 
    { 
     //get the base url or virtual directory of web app 
     var virtualDirectory = filterContext.Request.Headers.GetValues("vd").First(); 

     //base 64 encode the virtual directory 
     var encodedVirtualDirectory = Convert.ToBase64String(Encoding.UTF8.GetBytes(virtualDirectory)); 

     //set cookie name to look for to append '_' + base 64 encoded virtual directory to match how Asp.Net Html.AntiforgeryToken 
     //names the cookie that holds the token 
     var cookieName = "__RequestVerificationToken" + (String.IsNullOrEmpty(encodedVirtualDirectory) ? "" : "_" + ReplacePadding(encodedVirtualDirectory)); 

     //get the formtoken created by the Html.AntiforgeryToken helper (hidden input element in view) value sent in ajax header 
     var formtoken = filterContext.Request.Headers.GetValues("antiForgeryToken").First(); 

     //get the cookie token value with the name generated from above 
     var cookietoken = filterContext.Request.Headers.GetCookies(cookieName).FirstOrDefault()[cookieName].Value; 


     try 
     { 
      //Validate antiforgery token the old way because can't use ValidateAntiforgeryToken attribute with JSON content 
      System.Web.Helpers.AntiForgery.Validate(cookietoken, formtoken); 
      base.OnActionExecuting(filterContext); 
     } 
     catch 
     { 
      throw new ArgumentException("CSRF"); 
     } 


    } 

    //Supporting Method to strip base 64 '=' padding and replace with the number representation of padding 
    public string ReplacePadding(string b64) 
    { 
     var count = b64.Count(x => x == '='); 
     b64 = b64.Substring(0, b64.Length - count); 
     return b64 + count.ToString(); 
    } 
} 

这是我如何实现它在视图:

baseUrl = '@Url.Content("~/")'; 

     $.ajaxSetup({ 
      headers: { 
       antiForgeryToken: $('input[name="__RequestVerificationToken"]').val(), 
       vd: baseUrl.substring(0, baseUrl.length - 1) 
      } 
     }); 



       $.ajax({ 
        url: '@Url.Content("~/api/WebApiAction")', 
        data: JSON.stringify(paramObject), 
        contentType: "application/json", 
        type: 'PUT' 
       }) 
       .done(function (data) {/*do stuff*/});