ASP.NET MVC中如何保护继承基类的复杂对象免受批量赋值攻击
解决批量赋值问题的方案及属性保护建议
一、替代[Bind(Include)]的最佳方案:使用视图模型(ViewModel)
直接用[Bind(Include)]罗列大量属性确实会让代码臃肿且不易维护,最稳妥的方式是创建视图模型(ViewModel)——它只包含前端需要提交的属性,从根源上避免不必要的属性暴露,彻底解决批量赋值风险。
示例代码:
// 视图模型:仅保留创建员工时需要用户提交的字段 public class CreateEmployeeViewModel { public string FirstName { get; set; } public string LastName { get; set; } public string Position { get; set; } public string Department { get; set; } public string Gender { get; set; } public string Address { get; set; } public int Hours { get; set; } public bool IsPartTime { get; set; } // 若允许用户提交福利选项,就包含对应的属性 public bool Health { get; set; } public bool Vision { get; set; } public bool Dental { get; set; } public bool Life { get; set; } } // 修改后的AddEmployee方法 [HttpPost] public ActionResult AddEmployee(CreateEmployeeViewModel vm) { if (ModelState.IsValid) { // 手动将视图模型映射到Employee实体 var employee = new Employee { FirstName = vm.FirstName, LastName = vm.LastName, Position = vm.Position, Department = vm.Department, Gender = vm.Gender, Address = vm.Address, Hours = vm.Hours, IsPartTime = vm.IsPartTime, Health = vm.Health, Vision = vm.Vision, Dental = vm.Dental, Life = vm.Life // Salary、Id这类敏感/自动生成的属性,完全不在此处赋值 }; // 执行保存等后续逻辑 } return View("Home"); }
说明:视图模型精准控制了前端能提交的字段,像Salary(薪资)、Id(主键,通常由数据库自动生成)这类不需要用户输入的内容,根本不会出现在ViewModel中,自然无法被恶意赋值。
二、其他可选方案
1. 手动绑定请求参数
如果场景简单,也可以直接从Request中逐个提取允许的参数,再赋值给Employee:
[HttpPost] public ActionResult AddEmployee() { var employee = new Employee { FirstName = Request.Form["FirstName"], LastName = Request.Form["LastName"], Position = Request.Form["Position"], // 只绑定你允许的属性,跳过Salary、Id等敏感字段 }; // 验证及保存逻辑 return View("Home"); }
缺点是代码量略大,但适合小型项目或临时场景。
2. 使用DTO(数据传输对象)
和ViewModel思路一致,DTO更偏向服务层之间的数据传输,在前后端分离场景中同样能起到防批量赋值的作用,本质都是通过“窄化数据范围”来规避风险。
三、是否需要保护Employee和Benefits的属性?
是的,必须保护,可从两方面入手:
- 限制属性的可访问性:对于不需要外部直接修改的属性(如
Id、Salary),将其setter设为private或protected,只允许通过构造函数或特定方法赋值:
public class Employee : Benefits { public string FirstName { get; set; } public string LastName { get; set; } // Salary设为私有setter,仅能通过内部方法修改 public string Salary { get; private set; } public string Position { get; set; } public string Department { get; set; } public string Gender { get; set; } public string Address { get; set; } public int Hours { get; set; } // Id通常由数据库生成,设为私有setter public int Id { get; private set; } public bool IsPartTime { get; set; } // 提供修改薪资的方法,可添加验证逻辑 public void UpdateSalary(string newSalary) { if (!string.IsNullOrWhiteSpace(newSalary) && /* 额外验证逻辑 */) { Salary = newSalary; } } }
- 禁止直接将实体作为Action参数:直接接收
Employee实体的话,攻击者可以手动构造表单字段(比如Salary、Id)提交,即使前端没显示这些字段也能赋值。用ViewModel/DTO隔离实体和请求,才是更安全的做法。
内容的提问来源于stack exchange,提问作者Bill Kindig
相关产品推荐
相关产品推荐

