我使用EF代码优先方法来定义我的数据库结构。目前,我将EF实体类直接传递给MVC应用程序中的一些视图。这样可以很容易地填充和保存视图,因为我可以直接拥有一个存储库,为我提供填充的EF类,如果我的控制器在回发中收到EF实体类,我可以(如果验证可以)直接通过到存储库来保存它。但是,这可能存在安全隐患吗?如果实体类中有属性我不想修改,客户端是否可以将这些属性作为回发的一部分提交并修改它们?例如,假设我有一个视图来编辑我传递此EF模型的用户:
public class User {
[Required]
public string Firstname { get; set; }
[Required]
public string Surname { get; set; }
public DateTime DOB { get; set; }
public bool IsDisabled { get; set; }
}
我可能会将Firstname
,Surname
和DOB
公开为可编辑的表单字段,但我不希望用户能够设置IsDisabled
并禁用他们的帐户。防范这种情况的最佳方法是什么?也许人们只应该直接使用视图中的域模型,当人们认为该域模型所持有的每个属性都可由用户设置时,或者当一个人只是使用该域模型来显示内容时,而不是把东西保存回数据存储?
答案 0 :(得分:5)
是的,将实体直接传递给您的视图会很危险。从技术上讲,问题是当你直接模拟绑定到实体时。
是的,你提出的情况很有可能发生。更糟。假设您使用了User对象,那么攻击者可以提交post值来执行类似设置IsAdmin true的操作,或者更改分配给用户的角色。
当然,所有这些都取决于用户是否知道(或能够猜测)数据的结构。这可能不像看起来那么困难,因为我们经常在生成的HTML中提供告示标记。
这个问题有两种解决方案:
1)使用视图模型。视图模型仅包含视图中允许的数据。您还可以控制将哪些数据复制回实体模型。
2)您可以使用[Bind]
属性为白名单和黑名单各种属性指定排除和包含。
我更喜欢使用第一种方法,因为忘记列入白名单或黑名单会更加困难(特别是如果您稍后更改某些内容并忘记在绑定它的任何地方更新列表)。我也觉得[Bind]
作弊并鼓励邋design的设计。
答案 1 :(得分:3)
你并不是真的应该将你的实体模型用作视图模型,而且我认为你的例子确实比我在其他地方看到的更好。
如果您的管理员在发布后接受User
然后是,则您将公开要在那里发布的数据。如果您直接映射发布的用户而不检查无效的提交,那么您可以将问题持久存储到数据库中。
答案 2 :(得分:2)
您最好的选择是使用视图模型。这样,您和那些试图访问不可见属性的人都会受到保护,例如您IsDisabled
,因为它根本不在视图模型中,并且您将域与表示层分离。如果您需要扩展您的EF实体(更多属性,更多关系,等等),您的视图模型可能不需要更改。
答案 3 :(得分:0)
我刚看过这篇文章:
http://www.codethinked.com/ASPNET-MVC-Think-Before-You-Bind
它提供了几种处理此问题的方法,但我在项目中选择的方法是UpdateModel()
方法的变体。该方法依赖于从数据库中检索可更新对象,然后使用UpdateModel()
仅在该对象上设置要更新的字段。我的方法是使存储库的Update
方法采用lambda(“更新策略”)来负责设置要更新的字段:
public bool UpdateUser(User updatedUser, Action<User, User> updateStrategy) {
// Retrieve User via updatedUser.UserID...
// Update it using updateStrategy(retrievedUser, updatedUser)...
// Save the updated retrievedUser to DB
}
// ...
// To call the update method:
repoUser.UpdateUser(updatedUser, (existingUser, updatedUser) => {
existingUser.Firstname = updatedUser.Firstname;
existingUser.Surname = updatedUser.Surname;
existingUser.DOB = updatedUser.DOB;
});
在这两种情况下,它都会防止更新您不想要的任何字段,无论它们是否已发布到操作方法。当然,邪恶的客户可以修改隐藏的UserID
字段,但我不认为需要防范;它只是更改了哪个用户正在更新,如果你的系统允许他们更新用户,他们就没有权限,这似乎是一个安全漏洞本身。