2015-09-16 2 views
4

Ниже приведен фрагмент моего кодакак дезинфицировать входные данные в Web API с использованием анти XSS атаки

класса Модели

// Customer.cs

using CommonLayer; 

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

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

Я хочу, чтобы дезинфицировать значение в поле 'Name' класса модели как ниже

// 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', как показано ниже

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

Когда я вызываю метод класса «Пост» вышеуказанного контроллера, как показано ниже, это давая вызов методу «Пост» класса контроллера, как и ожидалось. Но это не вызов метода «BindProperty» в моем классе «CutstomModelBinder».

// 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

Чтобы дезинфицировать вход в общем виде с использованием Web API, вы можете создать свой собственный ModelBinder, как описано в моем предыдущем ответе, однако более простым подходом, вероятно, будет изменение существующего JsonMediaTypeFormatter, чтобы включить желаемую логику santization в метод ReadFromStreamAsync.

Один из подходов можно попробовать следующим образом:

Во-первых, создать общий атрибут, который используется для украшения свойств в пределах вашего 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 даже попадет на ваш контроллер.

Смежные вопросы