在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
昨天发表了一篇随笔《ASP.NET MVC: 使用 Filters 附加过滤敏感信息功能》(以下简称《Filter过滤》),今天一早醒来发现一处重大漏洞,于是在发了一条评论指出存在问题,并希望有朋友能指正。可到现在也没见有朋友找出问题,索引再发一篇随笔,进行更正。 《Filter过滤》一文中使用的代码如下:
1 public class SensitiveWordsFilterAttribute: ActionFilterAttribute
2 { 3 public override void OnActionExecuting(ActionExecutingContext filterContext) 4 { 5 var parameters = filterContext.ActionDescriptor.GetParameters(); 6 foreach (var parameter in parameters) 7 { 8 if (parameter.ParameterType == typeof(string)) 9 { 10 //获取字符串参数原值 11 var orginalValue = filterContext.ActionParameters[parameter.ParameterName] as string; 12 //使用过滤算法处理字符串 13 var filteredValue = SensitiveWordsFilter.Instance.Filter(orginalValue); 14 //将处理后值赋给参数 15 filterContext.ActionParameters[parameter.ParameterName] = filteredValue; 16 } 17 } 18 } 19 } 问题在第8行,SensitiveWordsFilterAttribute 目前只能对字符串类型这样的简单数据进行过滤,没有考虑到复杂类型,对下面代码就则无法实现过滤:
1 public class ArticlesController : Controller
2 { 3 [HttpPost] 4 public ActionResult Create(Article article) 5 { 6 //... 7 } 8 //... 9 } Article是一个自定义类:
1 public class Article: DomainModel
2 { 3 public int ID { get; set; } 4 public string Title { get; set; } 5 public string Content { get; set; } 6 public DateTime CreationTime { get; set; } 7 } SensitiveWordsFilterAttribute 对自定义类型是不太好处理的,当然也有解决办法,如使用反射遍历访问Article的每一个字符串属性,进行过滤。 本文采用 ASP.NET MVC Model Binding 来实现目标。 ASP.NET MVC Model Binding在ASP.NET MVC中,我们可以使用下面的方式来接收用户提交的数据:
1 public class ArticlesController : Controller
2 { 3 public ActionResult Query(string title, DateTime? creationDate) 4 { 5 //... 6 } 7 [HttpPost] 8 public ActionResult Create(Article article) 9 { 10 //... 11 } 12 } 这得益于 ASP.NET MVC ModelBinding 特性。使用这个特性,我们无需再使用 Request.Form["title"] 从 HTTP 请求中获取数据,也不需要再使用 DateTime.Parse() 或 DateTime.TryParse() 方法进行类型转换,也不再需要对实例的每一个属性逐一赋值。ModelBinding 特性将我们从这些无聊的低级工作中解放了出来,让我们专心去做高级的工作。 如上面代码所示,ModelBinding 既可以绑定简单数据类型,也可以绑定自定义类型,还可以绑定数组、集合、字典、二进制数据(byte[]),甚至还能用来接收文件上传。 ModelBinding 在绑定自定义类型时,还能够有选择的绑定指定属性或排除指定属性:
1 public ActionResult Create([Bind(Include = "Name, Sex, Birthday")] Person person)
2 { 3 // ... 4 } 5 public ActionResult Create([Bind(Exclude = "ID")] Person person) 6 { 7 // ... 8 } ASP.NET MVC ModelBinding 的功能相当完善,实现也比较复杂,具体是由 DefaultModelBinder 类来完成的。对于一些特定功能(如本文中的敏感信息过滤)可通过扩展 DefaultModelBinder 类的功能来实现。 具有敏感信息过滤功能 ModelBinder在面向对象的世界中,扩展功能最简单的方式就是继承。我们新建一个类 SensitiveWordsFilterModelBinder,继承自 DefaultModelBinder,重写 SetPropertyt 和 BindModel 方法,如下:
1 public class SensitiveWordsFilterModelBinder : DefaultModelBinder
2 { 3 protected override void SetProperty(ControllerContext controllerContext, 4 ModelBindingContext bindingContext, PropertyDescriptor propertyDescriptor, object value) 5 { 6 if (propertyDescriptor.PropertyType == typeof(string)/* && value is string*/) 7 value = SensitiveWordsFilter.Instance.Filter(value as string); 8 base.SetProperty(controllerContext, bindingContext, propertyDescriptor, value); 9 } 10 public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) 11 { 12 var value = base.BindModel(controllerContext, bindingContext); 13 if(bindingContext.ModelType == typeof(string)/* && value is string*/) 14 value = SensitiveWordsFilter.Instance.Filter(value as string); 15 return value; 16 } 17 } 重写 SetProperty 方法(3~9行)用来对自定义类型进行敏感信息过滤:对自定义类型来说,数据绑定主要针对属性,所有属性绑定完成,自定义类型的实例也就绑定完成了。重写 SetProperty 就是在给 string 类型的属性赋值前,增加敏感信息过滤这一步操作。相对使用 SensitiveWordsFilterAttribute + 反射 的方式处理自定义类型,SensitiveWordsFilterModelBinder 更加合理,效率也要高些。重写 SetProperty 时,可以通过 bindingContext.ModelType 获取所在类的类型,也可以通过 propertyDescriptor 获取当前属性的信息,还可以通过controllerContext获取当前controller的信息,通过这三个属性,我们可以有选择的进行过滤操作,以提高效率。 重写 BindModel 方法(10~16行)用来对 string 类型进行敏感信息过滤,string 是简单数据库类型,对它进行绑定时不会调用 SetProperty 方法,因此要单独进行处理。 仅仅有这个 ModelBinder 是不够的,MVC不知道什么情况下使用它,因此我们还要进行配置,使 SensitiveWordsFilterModelBinder 生效。 配置使用 SensitiveWordsFilterModelBinder方式一:直接在参数上使用
1 public ActionResult Create([ModelBinder(typeof(SensitiveWordsFilterModelBinder))]Article article)
2 { 3 return null; 4 } 5 public ActionResult Create([ModelBinder(typeof(SensitiveWordsFilterModelBinder))]string title, 6 [ModelBinder(typeof(SensitiveWordsFilterModelBinder))]string content, DateTime? creationTime) 7 { 8 return null; 9 } 使用这种方式比较“ugly”,尤其是 ModelBinder 名字比较长的时候。这种方式的最大缺点是需要对每个参数进行标记。 方式二:标记在 Model 上
1 [ModelBinder(typeof(SensitiveWordsFilterModelBinder))]
2 public class Article: DomainModel 3 { 4 //... 5 } 这种比较不错,只需在 Article 类上标记一次,所有 Controller 中的 Action 的 Article 类型的参数都将使用 SensitiveWordsFilterModelBinder。 但这种方式仅对自定义类型有效,对系统定义的类型,如:string,我们是很难给它加上 Attribute 的。 方式三:在 Global.asax.cs 文件中处理:
1 protected void Application_Start()
2 { 3 ModelBinders.Binders.Add(typeof(Article), new SensitiveWordsFilterModelBinder()); 4 ModelBinders.Binders.Add(typeof(string), new SensitiveWordsFilterModelBinder()); 5 } 如上面代码,我们可为每一种类型,设置一个 ModerBinder。也可以像下面这样进行批量设置:
1 protected void Application_Start()
2 { 3 var sensitiveWordsFilterModelBinder = new SensitiveWordsFilterModelBinder(); 4 var types = typeof(Article).Assembly.GetTypes().Where(t=>t.IsSubclassOf(typeof(DomainModel))); 5 foreach (var type in types) 6 ModelBinders.Binders.Add(type, sensitiveWordsFilterModelBinder); 7 } 这个就不多解释了。 使用 SensitiveWordsFilterModelBinder 方式的缺点有些 MVC 的初学者之前大多是做 ASP.NET,可能有时会使用 Request.Form["..."] 这种方式来接收数据,使用这种方式的地方都会成为系统的漏洞。 总结ASP.Net MVC 采用了非常先进的设计思想,具有良好的可扩展性,可以通过各种方式轻松解决我们遇到的各种问题。 昨天发表了一篇随笔《ASP.NET MVC: 使用 Filters 附加过滤敏感信息功能》(以下简称《Filter过滤》),今天一早醒来发现一处重大漏洞,于是在发了一条评论指出存在问题,并希望有朋友能指正。可到现在也没见有朋友找出问题,索引再发一篇随笔,进行更正。 《Filter过滤》一文中使用的代码如下:
1 public class SensitiveWordsFilterAttribute: ActionFilterAttribute
2 { 3 public override void OnActionExecuting(ActionExecutingContext filterContext) 4 { 5 var parameters = filterContext.ActionDescriptor.GetParameters(); 6 foreach (var parameter in parameters) 7 { 8 if (parameter.ParameterType == typeof(string)) 9 { 10 //获取字符串参数原值 11 var orginalValue = filterContext.ActionParameters[parameter.ParameterName] as string; 12 //使用过滤算法处理字符串 13 var filteredValue = SensitiveWordsFilter.Instance.Filter(orginalValue); 14 //将处理后值赋给参数 15 filterContext.ActionParameters[parameter.ParameterName] = filteredValue; 16 } 17 } 18 } 19 } 问题在第8行,SensitiveWordsFilterAttribute 目前只能对字符串类型这样的简单数据进行过滤,没有考虑到复杂类型,对下面代码就则无法实现过滤:
1 public class ArticlesController : Controller
2 { 3 [HttpPost] 4 public ActionResult Create(Article article) 5 { 6 //... 7 } 8 //... 9 } Article是一个自定义类:
1 public class Article: DomainModel
2 { 3 public int ID { get; set; } 4 public string Title { get; set; } 5 public string Content { get; set; } 6 public DateTime CreationTime { get; set; } 7 } SensitiveWordsFilterAttribute 对自定义类型是不太好处理的,当然也有解决办法,如使用反射遍历访问Article的每一个字符串属性,进行过滤。 本文采用 ASP.NET MVC Model Binding 来实现目标。 ASP.NET MVC Model Binding在ASP.NET MVC中,我们可以使用下面的方式来接收用户提交的数据:
1 public class ArticlesController : Controller
2 { 3 public ActionResult Query(string title, DateTime? creationDate) 4 { 5 //... 6 } 7 [HttpPost] 8 public ActionResult Create(Article article) 9 { 10 //... 11 } 12 } 这得益于 ASP.NET MVC ModelBinding 特性。使用这个特性,我们无需再使用 Request.Form["title"] 从 HTTP 请求中获取数据,也不需要再使用 DateTime.Parse() 或 DateTime.TryParse() 方法进行类型转换,也不再需要对实例的每一个属性逐一赋值。ModelBinding 特性将我们从这些无聊的低级工作中解放了出来,让我们专心去做高级的工作。 如上面代码所示,ModelBinding 既可以绑定简单数据类型,也可以绑定自定义类型,还可以绑定数组、集合、字典、二进制数据(byte[]),甚至还能用来接收文件上传。 ModelBinding 在绑定自定义类型时,还能够有选择的绑定指定属性或排除指定属性:
1 public ActionResult Create([Bind(Include = "Name, Sex, Birthday")] Person person)
2 { 3 // ... 4 } 5 public ActionResult Create([Bind(Exclude = "ID")] Person person) 6 { 7 // ... 8 } ASP.NET MVC ModelBinding 的功能相当完善,实现也比较复杂,具体是由 DefaultModelBinder 类来完成的。对于一些特定功能(如本文中的敏感信息过滤)可通过扩展 DefaultModelBinder 类的功能来实现。 具有敏感信息过滤功能 ModelBinder在面向对象的世界中,扩展功能最简单的方式就是继承。我们新建一个类 SensitiveWordsFilterModelBinder,继承自 DefaultModelBinder,重写 SetPropertyt 和 BindModel 方法,如下:
1 public class SensitiveWordsFilterModelBinder : DefaultModelBinder
2 { 3 protected override void SetProperty(ControllerContext controllerContext, 4 ModelBindingContext bindingContext, PropertyDescriptor propertyDescriptor, object value) 5 { 6 if (propertyDescriptor.PropertyType == typeof(string)/* && value is string*/) 7 value = SensitiveWordsFilter.Instance.Filter(value as string); 8 base.SetProperty(controllerContext, bindingContext, propertyDescriptor, value); 9 } 10 public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) 11 { 12 var value = base.BindModel(controllerContext, bindingContext); 13 if(bindingContext.ModelType == typeof(string)/* && value is string*/) 14 value = SensitiveWordsFilter.Instance.Filter(value as string); 15 return value; 16 } 17 } 重写 SetProperty 方法(3~9行)用来对自定义类型进行敏感信息过滤:对自定义类型来说,数据绑定主要针对属性,所有属性绑定完成,自定义类型的实例也就绑定完成了。重写 SetProperty 就是在给 string 类型的属性赋值前,增加敏感信息过滤这一步操作。相对使用 SensitiveWordsFilterAttribute + 反射 的方式处理自定义类型,SensitiveWordsFilterModelBinder 更加合理,效率也要高些。重写 SetProperty 时,可以通过 bindingContext.ModelType 获取所在类的类型,也可以通过 propertyDescriptor 获取当前属性的信息,还可以通过controllerContext获取当前controller的信息,通过这三个属性,我们可以有选择的进行过滤操作,以提高效率。 重写 BindModel 方法(10~16行)用来对 string 类型进行敏感信息过滤,string 是简单数据库类型,对它进行绑定时不会调用 SetProperty 方法,因此要单独进行处理。 仅仅有这个 ModelBinder 是不够的,MVC不知道什么情况下使用它,因此我们还要进行配置,使 SensitiveWordsFilterModelBinder 生效。 配置使用 SensitiveWordsFilterModelBinder方式一:直接在参数上使用
1 public ActionResult Create([ModelBinder(typeof(SensitiveWordsFilterModelBinder))]Article article)
2 { 3 return null; 4 } 5 public ActionResult Create([ModelBinder(typeof(SensitiveWordsFilterModelBinder))]string title, 6 [ModelBinder(typeof(SensitiveWordsFilterModelBinder))]string content, DateTime? creationTime) 7 { 8 return null; 9 } 使用这种方式比较“ugly”,尤其是 ModelBinder 名字比较长的时候。这种方式的最大缺点是需要对每个参数进行标记。 方式二:标记在 Model 上
1 [ModelBinder(typeof(SensitiveWordsFilterModelBinder))]
2 public class Article: DomainModel 3 { 4 //... 5 } 这种比较不错,只需在 Article 类上标记一次,所有 Controller 中的 Action 的 Article 类型的参数都将使用 SensitiveWordsFilterModelBinder。 但这种方式仅对自定义类型有效,对系统定义的类型,如:string,我们是很难给它加上 Attribute 的。 方式三:在 Global.asax.cs 文件中处理:
1 protected void Application_Start()
2 { 3 ModelBinders.Binders.Add(typeof(Article), new SensitiveWordsFilterModelBinder()); 4 ModelBinders.Binders.Add(typeof(string), new SensitiveWordsFilterModelBinder()); 5 } 如上面代码,我们可为每一种类型,设置一个 ModerBinder。也可以像下面这样进行批量设置:
1 protected void Application_Start()
2 { 3 var sensitiveWordsFilterModelBinder = new SensitiveWordsFilterModelBinder(); 4 var types = typeof(Article).Assembly.GetTypes().Where(t=>t.IsSubclassOf(typeof(DomainModel))); 5 foreach (var type in types) 6 ModelBinders.Binders.Add(type, sensitiveWordsFilterModelBinder); 7 } 这个就不多解释了。 使用 SensitiveWordsFilterModelBinder 方式的缺点有些 MVC 的初学者之前大多是做 ASP.NET,可能有时会使用 Request.Form["..."] 这种方式来接收数据,使用这种方式的地方都会成为系统的漏洞。 总结ASP.Net MVC 采用了非常先进的设计思想,具有良好的可扩展性,可以通过各种方式轻松解决我们遇到的各种问题。 |
请发表评论