如何使用反XSS攻击清理Web API中的输入数据 [英] how to sanitize input data in web api using anti xss attack

查看:160
本文介绍了如何使用反XSS攻击清理Web API中的输入数据的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

下面是我的代码段

模型类

//Customer.cs

// Customer.cs

using CommonLayer;

namespace Models
{
    public class Customer
    {
        public int Id { get; set; }

        [MyAntiXss]
        public string Name { get; set; }
    }
}

我要对Model类的名称"字段中的值进行清理,如下所示

I want to sanitize the value in the 'Name' field of the Model class as below

//CutstomModelBinder.cs

// CutstomModelBinder.cs

 using Microsoft.Security.Application;
    using System.ComponentModel;
    using System.Linq;
    using System.Web.Mvc;

    namespace CommonLayer
    {
        public class CutstomModelBinder : DefaultModelBinder
        {
            protected override void BindProperty(ControllerContext controllerContext, ModelBindingContext bindingContext, PropertyDescriptor propertyDescriptor)
            {
                if (propertyDescriptor.Attributes.OfType<MyAntiXssAttribute>().Any())
                {
                    ValueProviderResult valueResult = bindingContext.ValueProvider.GetValue(propertyDescriptor.Name);
                    string filteredValue = Encoder.HtmlEncode(valueResult.AttemptedValue);
                    propertyDescriptor.SetValue(bindingContext.Model, filteredValue);
                }
                else
                    base.BindProperty(controllerContext, bindingContext, propertyDescriptor);
            }
        }
    }

我如下将"DefaultBinder"更改为"CutstomModelBinder"

I changed the 'DefaultBinder' to my 'CutstomModelBinder' as below

//Global.asax.cs

// Global.asax.cs

using CommonLayer;
using System.Web.Http;
using System.Web;
using System.Web.Mvc;

namespace WebAPI
{
    public class WebApiApplication : HttpApplication
    {
        protected void Application_Start()
        {
            GlobalConfiguration.Configure(WebApiConfig.Register);
            ModelBinders.Binders.DefaultBinder = new CutstomModelBinder();
        }
    }
}

我写了一个如下的控制器类

I wrote a controller class as below

//CustomerController.cs

// CustomerController.cs

using Models;
using System.Collections.Generic;
using System.Web.Http;

namespace WebAPI.Controllers
{
    public class CustomerController : ApiController
    {
        public string Post([FromBody]Customer customer)
        {
            //customer.Name = Encoder.HtmlEncode(customer.Name);
            return string.Format("Id = {0}, Name = '{1}'", customer.Id, customer.Name);
        }
    }
}

当我如下调用上述控制器类的"Post"方法时,它正在按预期方式调用该控制器类的"Post"方法.但这不是在我的"CutstomModelBinder"类中调用"BindProperty"方法.

When I am calling the above controller's class 'Post' method as below, it is giving call to the 'Post' method of the controller's class as expected. But it is not calling the 'BindProperty' method in my 'CutstomModelBinder' class.

//Program.cs

// Program.cs

using Models;
using System;
using System.Net.Http;
using System.Net.Http.Formatting;
using System.Net.Http.Headers;

namespace Client
{
    public static class Program
    {
        public static void Main(params string[] args)
        {
            bool success = Post();
            Console.WriteLine("success = " + success);
            Console.Read();
        }

        private static HttpClient GetHttpClient()
        {
            HttpClient client = new HttpClient { BaseAddress = new Uri("http://localhost:49295/") };
            client.DefaultRequestHeaders.Accept.Clear();
            client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
            return client;
        }

        private static bool Post()
        {
            Customer customer = new Customer { Id = 1, Name = "<br>Anivesh</br>" };
            HttpContent content = new ObjectContent<Customer>(customer, new JsonMediaTypeFormatter());

            HttpClient client = GetHttpClient();
            HttpResponseMessage response = client.PostAsync("Customer", content).Result;
            client.Dispose();

            if (response.IsSuccessStatusCode)
            {
                string expected = string.Format("Id = {0}, Name = '{1}'", customer.Id, customer.Name);
                string result = response.Content.ReadAsAsync<string>().Result;
                return expected == result;
            }
            else
                return false;
        }
    }
}

请让我知道使用"DataBinders"的正确方法,以便在接收控制器中的调用之前,可以在一个公共位置清理输入数据.

Please let me know the correct way of using the 'DataBinders', so that I could sanitize the input data at a common place, before receiving calls in the controllers.

推荐答案

要使用Web API以通用的方式清理输入,可以按照我先前的回答中所述创建自己的ModelBinder,但是更简单的方法可能是修改现有的JsonMediaTypeFormatter,以在 ReadFromStreamAsync 方法中包含所需的分隔符逻辑.

To sanitize input in a generic fashion using Web API, you could create your own ModelBinder as described in my previous answer, however an easier approach would likely be to modify the existing JsonMediaTypeFormatter to include the desired santization logic within the ReadFromStreamAsync method.

您可以尝试的一种方法如下:

One approach you could try is as follows:

首先,创建一个通用属性,该属性用于装饰需要清理的DTO中的属性,即:

First, create a generic Attribute which is used to decorate the properties within your DTO that requires sanitization, ie:

 [AttributeUsage(AttributeTargets.Property)]
 public sealed class SanitizeAttribute : Attribute
 { }

然后创建负责清洁的JsonMediaTypeFormatter的子类型,即:

Then create a sub-type of the JsonMediaTypeFormatter which takes care of the sanitization, ie:

public sealed class SanitizingJsonMediaTypeFormatter : JsonMediaTypeFormatter
{
    public override Task<object> ReadFromStreamAsync(Type type, Stream readStream, HttpContent content, IFormatterLogger formatterLogger, CancellationToken cancellationToken)
    {
        Task<object> resultTask = base.ReadFromStreamAsync(type, readStream, content, formatterLogger, cancellationToken);

        var propertiesFlaggedForSanitization = type.GetProperties().Where(e => e.GetCustomAttribute<SanitizeAttribute>() != null).ToList();
        if (propertiesFlaggedForSanitization.Any())
        {
            var result = resultTask.Result;
            foreach (var propertyInfo in propertiesFlaggedForSanitization)
            {
                var raw = (string)propertyInfo.GetValue(result);
                if (!string.IsNullOrEmpty(raw))
                {
                    propertyInfo.SetValue(result, AntiXssEncoder.HtmlEncode(raw, true));
                }
            }
        }
        return resultTask;
    }
}

此实现仅检查生成的Type是否具有用Sanitize属性修饰的任何属性,如果是,则使用内置的System.Web.Security.AntiXss.AntiXssEncoder(.NET 4.5及更高版本)来进行消毒.

This implementation simply checks to see if the resulting Type has any properties that are decorated with the Sanitize attribute, and if so, uses the built-in System.Web.Security.AntiXss.AntiXssEncoder (.NET 4.5 and above) to perform the sanitization.

您可能需要优化此类,以便其缓存类型和属性信息,这样您就不必在每次反序列化时都进行繁重的反射调用.

You'll likely will want to optimize this class such that it caches type and property information such that you're not doing heavy weight reflection calls on each deserialization.

该过程的最后一步是在WebAPI启动代码中用您自己的内置JSON媒体类型格式化程序替换

The last step in the process is to replace the built-in JSON media type formatter with your own, within the WebAPI start-up code:

var jsonFormatter = config.Formatters.OfType<JsonMediaTypeFormatter>().First();
config.Formatters.Remove(jsonFormatter);
config.Formatters.Add(new SanitizingJsonMediaTypeFormatter());

现在,所有具有用Sanitize属性修饰的属性的DTO都将在DTO到达您的控制器之前被正确编码.

Now any DTO that has properties decorated with the Sanitize attribute will be properly encoded before the DTO even hits your controller.

这篇关于如何使用反XSS攻击清理Web API中的输入数据的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆