Ank*_*ani 7 c# xss modelbinder asp.net-web-api
以下是我的代码片段
模型类
// Customer.cs
using CommonLayer;
namespace Models
{
public class Customer
{
public int Id { get; set; }
[MyAntiXss]
public string Name { get; set; }
}
}
Run Code Online (Sandbox Code Playgroud)
我想清理Model类的'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);
}
}
}
Run Code Online (Sandbox Code Playgroud)
我将'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();
}
}
}
Run Code Online (Sandbox Code Playgroud)
我写了一个控制器类,如下所示
// 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);
}
}
}
Run Code Online (Sandbox Code Playgroud)
当我按如下方式调用上述控制器的类'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;
}
}
}
Run Code Online (Sandbox Code Playgroud)
请让我知道使用'DataBinders'的正确方法,以便在接收控制器中的调用之前,我可以在公共位置清理输入数据.
要使用Web API以通用方式清理输入,您可以按照我之前的答案中所述创建自己的ModelBinder,但更简单的方法可能是修改现有的JsonMediaTypeFormatter以在ReadFromStreamAsync方法中包含所需的santization逻辑.
您可以尝试的一种方法如下:
首先,创建一个通用属性,用于装饰需要清理的DTO中的属性,即:
[AttributeUsage(AttributeTargets.Property)]
public sealed class SanitizeAttribute : Attribute
{ }
Run Code Online (Sandbox Code Playgroud)
然后创建一个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;
}
}
Run Code Online (Sandbox Code Playgroud)
此实现只是检查生成的Type是否具有使用Sanitize属性修饰的任何属性,如果是,则使用内置的System.Web.Security.AntiXss.AntiXssEncoder(.NET 4.5及更高版本)执行清理.
您可能希望优化此类,以便缓存类型和属性信息,这样您就不会对每个反序列化进行重量级反射调用.
该过程的最后一步是在WebAPI启动代码中用您自己的内置JSON媒体类型格式化程序替换:
var jsonFormatter = config.Formatters.OfType<JsonMediaTypeFormatter>().First();
config.Formatters.Remove(jsonFormatter);
config.Formatters.Add(new SanitizingJsonMediaTypeFormatter());
Run Code Online (Sandbox Code Playgroud)
现在任何具有使用Sanitize属性修饰的属性的DTO都将在DTO甚至命中控制器之前被正确编码.
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;
}
}
Run Code Online (Sandbox Code Playgroud)
可以在此处找到支持此示例的完整帖子: MSDN 模型绑定
| 归档时间: |
|
| 查看次数: |
7318 次 |
| 最近记录: |