昨天发表了一篇随笔《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,重写 SetProperty 和 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 采用了非常先进的设计思想,具有良好的可扩展性,可以通过各种方式轻松解决我们遇到的各种问题。

转载于:https://www.cnblogs.com/ldp615/archive/2010/07/30/SensitiveWordsFilterModelBinder.html

ASP.NET MVC: 使用自定义 ModelBinder 过滤敏感信息相关推荐

  1. asp.net MVC之 自定义过滤器(Filter)

    一.系统过滤器使用说明 1.OutputCache过滤器 OutputCache过滤器用于缓存你查询结果,这样可以提高用户体验,也可以减少查询次数.它有以下属性: Duration:缓存的时间,以秒为 ...

  2. 【译】Asp.net MVC 利用自定义RouteHandler来防止图片盗链 (转)

    你曾经注意过在你服务器请求日志中多了很多对图片资源的请求吗?这可能是有人在他们的网站中盗链了你的图片所致,这会占用你的服务器带宽.下面这种方法可以告诉你如何在ASP.NET MVC中实现一个自定义Ro ...

  3. [ASP.NET MVC] 利用自定义的AuthenticationFilter实现Basic认证

    很多情况下目标Action方法都要求在一个安全上下文中被执行,这里所谓的安全上下文主要指的是当前请求者是一个经过授权的用户.授权的本质就是让用户在他许可的权限范围内做他能够做的事情,授权的前提是请求者 ...

  4. ASP.NET MVC - 设置自定义IIdentity或IPrincipal

    我需要做一些相当简单的事情:在我的ASP.NET MVC应用程序中,我想设置一个自定义IIdentity / IPrincipal. 哪个更容易/更合适. 我想扩展默认值,以便我可以调用User.Id ...

  5. asp.net mvc(十一)自定义view engine

    当创建一个asp.net mvc 1.0的项目后,在web工程中都会出现Views文件夹,这里面就是我们存放View Page或者是partial view的地方.而且系统对于Controller的名 ...

  6. ASP.NET MVC和Bootstrap制作一个员工信息表

    新建一个asp.net mvc的项目,然后在解决方案下添加以下几个类库: BLL中是业务逻辑代码,Commons是公共的代码(比如MD5加密),DAL数据访问代码,DBUtility是数据库连接代码, ...

  7. ASP.NET MVC过滤器自定义Authorize实现身份验证

    最近在做一个.NET MVC框架的后台项目,涉及到5个权限,而控制器下的方法有些只能高权限用户使用,低权限用户不能访问,一开始是在方法里面写判断,但是功能方法多了以后,发现代码重复的很严重,不符合面向 ...

  8. ASP.NET MVC ActionFilter自定义过滤器异常过滤器过滤器用法

    自定义过滤器使用非常灵活,可以精确的注入到请求前.请求中和请求后.继承抽象类ActionFilterAttribute并重写里面的方法即可: public class SystemLogAttribu ...

  9. asp.net mvc 实现自定义AuthorizeAttribute(权限验证)标签 无效的一种原因

    ApiController下的授权筛选attribute为System.Web.Http.AuthorizeAttribute 普通Controller用的是System.Web.Mvc.Author ...

最新文章

  1. 面试官:你知道Java中的回调机制吗?
  2. Windows PE第6章 栈与重定位表
  3. Oracle Drop表并未直接删除 drop table xx purge
  4. BF算法和KMP算法实现
  5. 【机器学习】GBDT 与 LR 的区别总结
  6. SpringBoot 2.x 整合Lombok
  7. LeetCode 1922. 统计好数字的数目(快速幂)
  8. 最近很火的数据动图python_GitHub热榜第一,标星近万:这个用Python做交互式图形的项目火了...
  9. 天逸ad一66da_深入解析天逸ad66d与ad66a哪个好?区别是?内幕评测吐槽
  10. oracle获取SID
  11. 数据结构 —— 递归和树
  12. css属性之box-shadow
  13. yum提示Another app is currently holding the yum lock
  14. 包无法安装_BiocManager无法安装R包
  15. 《浪潮之巅》11~14章
  16. 人工智能基础 | Python实现-产生式系统-文字动物识别系统
  17. exFAT硬盘写保护修复远程登录提示到期
  18. 巧妙地进行非线性拟合——非线性拟合转化为线性拟合
  19. 我是一个硬盘(很感人)
  20. java aspx 验证码,asp 动态生成验证码

热门文章

  1. moto linux手机,moto linux手机目录简解
  2. 他们的背后,是我们!
  3. HDU 1407 测试你是否和LTC水平一样高 (HASH)
  4. Netty - 传输
  5. 工作区 暂存区 版本库之间的关系
  6. 零元学Expression Blend 4 - Chapter 1 缘起
  7. push an existing repository from the command line
  8. ionic.css界面组件:表单-复选按钮
  9. 4.羽翼sqlmap学习笔记之Post登录框注入
  10. 回头看医疗行业信息化,怎一个乱字了得