【问题标题】:Prevent CSRF Attack for PUT and Delete request ASP.NET Web API防止针对 PUT 和删除请求的 CSRF 攻击 ASP.NET Web API
【发布时间】:2013-04-11 22:45:12
【问题描述】:

ValidateAntiForgeryToken 是否适用于 PUT 和 Delete 请求或仅适用于 ASP.NET Web API 中的 post 请求?如果不是,那么确保其安全的最佳方法是什么?

【问题讨论】:

    标签: asp.net-mvc security asp.net-web-api csrf


    【解决方案1】:

    反 CSRF 通常是通过匹配来自 cookie 和 body 的令牌来验证非 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);
    }
    

    【讨论】:

      【解决方案2】:

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

      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*/});
      

      【讨论】:

        猜你喜欢
        • 2015-07-08
        • 1970-01-01
        • 1970-01-01
        • 2013-10-06
        • 1970-01-01
        • 2015-01-15
        • 2011-03-12
        • 2014-08-27
        • 2016-09-12
        相关资源
        最近更新 更多