2015-09-16 241 views
4

下面来消毒的Web API输入数据是我的代码片段如何使用防XSS攻击

Model类

// Customer.cs

using CommonLayer; 

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

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

我要消毒的价值在模型类,如下

// CutstomModelBinder.cs

的“名称”字段

我改变了 'DefaultBinder' 我的 'CutstomModelBinder' 如下

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

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

// 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'方法。

// 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”的正确方式,这样我就可以在一个共同的地方净化输入数据,接收控制器之前调用。

回答

0

DefaultModelBinder位于System.Web.ModelBinding命名空间中,该命名空间由MVC控制器使用。

对于WebAPI项目,您需要实现System.Web.Http.ModelBinding.IModelBinder接口。

样本模型绑定,从MSDN网站权而采取如下:

public class GeoPointModelBinder : IModelBinder 
{ 
    // List of known locations. 
    private static ConcurrentDictionary<string, GeoPoint> _locations 
     = new ConcurrentDictionary<string, GeoPoint>(StringComparer.OrdinalIgnoreCase); 

    static GeoPointModelBinder() 
    { 
     _locations["redmond"] = new GeoPoint() { Latitude = 47.67856, Longitude = -122.131 }; 
     _locations["paris"] = new GeoPoint() { Latitude = 48.856930, Longitude = 2.3412 }; 
     _locations["tokyo"] = new GeoPoint() { Latitude = 35.683208, Longitude = 139.80894 }; 
    } 

    public bool BindModel(HttpActionContext actionContext, ModelBindingContext bindingContext) 
    { 
     if (bindingContext.ModelType != typeof(GeoPoint)) 
     { 
      return false; 
     } 

     ValueProviderResult val = bindingContext.ValueProvider.GetValue(
      bindingContext.ModelName); 
     if (val == null) 
     { 
      return false; 
     } 

     string key = val.RawValue as string; 
     if (key == null) 
     { 
      bindingContext.ModelState.AddModelError(
       bindingContext.ModelName, "Wrong value type"); 
      return false; 
     } 

     GeoPoint result; 
     if (_locations.TryGetValue(key, out result) || GeoPoint.TryParse(key, out result)) 
     { 
      bindingContext.Model = result; 
      return true; 
     } 

     bindingContext.ModelState.AddModelError(
      bindingContext.ModelName, "Cannot convert value to Location"); 
     return false; 
    } 
} 

,备份这个样本的完整后可以在这里找到:MSDN Model Binding

5

要使用一种通用的方式净化输入网络API,你可以在我以前的答案说明,但更简单的方法很可能是修改现有的JsonMediaTypeFormatter到包括ReadFromStreamAsync方法中所需的santization逻辑创建自己的ModelBinder的。

一种方法,你可以尝试如下:

首先,创建它用于您的DTO内装饰性的通用属性,需要消毒,即:

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

然后创建一个子在JsonMediaTypeFormatter的型这需要杀毒的照顾,即:

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; 
    } 
} 

此实现简单地检查,看是否导致类型有任何亲使用Sanitize属性修饰的属性,如果是,则使用内置的System.Web.Security.AntiXss.AntiXssEncoder(.NET 4.5及更高版本)来执行清理。

您可能会希望优化此类,以便缓存类型和属性信息,以便在每个反序列化过程中不会进行重量级的反射调用。

在这个过程的最后一步是更换内置JSON的媒体类型格式化用自己的的WebAPI启动代码中:

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

现在拥有的属性饰有消毒任何DTO属性将在DTO甚至碰到你的控制器之前正确编码。