简体   繁体   English

MVC C#:验证当前用户有权查看/更新数据

[英]MVC C#: Authenticating current user has rights to view/update data

Our company is currently remaking our software from web forms to MVC and it is currently in alpha stage before we release it to our clients.我们公司目前正在将我们的软件从 Web 表单重新制作为 MVC,并且在我们将其发布给我们的客户之前,它目前处于 alpha 阶段。 In a nutshell, the app have multiple hotels subscribing and using our service.简而言之,该应用程序有多家酒店订阅和使用我们的服务。 The core security I now need to ensure is that no user should be able to access/modify data belonging to another hotel.我现在需要确保的核心安全是没有用户应该能够访问/修改属于另一家酒店的数据。

Our models mostly follow either one of the two examples below:我们的模型大多遵循以下两个示例之一:

RoomCategory   (structure: model.HotelID)
--------------
ID
HotelID

Room    (structure: model.Parent.HotelID)
--------------
ID
RoomCategoryID
HotelID

The problem is that when I am on ~/rooms/edit/1, I can change the form action URL and the hidden field value from '1' to '50' and room ID 50 belongs to another hotel.问题是,当我在 ~/rooms/edit/1 上时,我可以将表单操作 URL 和隐藏字段值从“1”更改为“50”,并且房间 ID 50 属于另一家酒店。 This is a big problem since one user can actually 'steal' a room from another hotel and make it their own!这是一个大问题,因为一个用户实际上可以从另一家酒店“窃取”一个房间并将其变成自己的房间! Our clients will not be very happy.我们的客户不会很高兴。

The way I'm approaching the problem...我处理问题的方式...

From the currently logged-in user data (accessed from session), we know the Hotel (or Hotels) the user has the rights to manage.从当前登录的用户数据(从会话访问),我们知道用户有权管理的酒店(或酒店)。 One way is to authenticate each action call and do something like this:一种方法是验证每个动作调用并执行如下操作:

app.AuthenticateAccess(room.RoomCategory.HotelID);

This way, the AuthenticateAccess function will prevent further operation and 'redirect' to an Unauthorized/NotFound page since it knows that Room ID 50 belongs to HotelID 2 while the current user doesn't have access to it.这样,AuthenticateAccess 函数将阻止进一步操作并“重定向”到 Unauthorized/NotFound 页面,因为它知道 Room ID 50 属于 HotelID 2 而当前用户无权访问它。 Sure I think this is a safe way, but this involves a lot of duplicate function calls across all actions in each controller.当然,我认为这是一种安全的方法,但这涉及每个控制器中所有操作的大量重复函数调用。

I have been looking into different possibilities to overcome this security challenge at a global level:我一直在寻找在全球范围内克服这一安全挑战的不同可能性:

  1. Easiest.最简单。 Encrypt all IDs and hidden fields value though I personally think that even without encryption, the system should be able to authenticate the access each user has.加密所有 ID 和隐藏字段值,尽管我个人认为即使没有加密,系统也应该能够验证每个用户的访问权限。
  2. Set HotelID in a separate abstract class and let each model (which stores data about one particular hotel) inherits from this abstract class.在一个单独的抽象类中设置 HotelID,并让每个模型(存储关于一个特定酒店的数据)从这个抽象类继承。 Perhaps some kind of generics can be done here?也许可以在这里完成某种泛型?
  3. We use repository/unit of work pattern.我们使用存储库/工作单元模式。 Is there some way to limit the data which we can retrieve/update to that of the currently selected Hotel ID only?是否有某种方法可以将我们可以检索/更新的数据限制为仅当前所选酒店 ID 的数据?
  4. [Your awesome solution goes here.] [你很棒的解决方案在这里。]

I have some other solutions in mind like using System.Reflection to search for all HotelID attributes and ensuring all the data saved/created is allowed for the current user to make.我有一些其他的解决方案,比如使用 System.Reflection 搜索所有 HotelID 属性并确保允许当前用户创建所有保存/创建的数据。 But anyway let me hear your approach to solve this issue since I am not convinced yet with any solution that I can think of.但无论如何,让我听听您解决这个问题的方法,因为我还不相信我能想到的任何解决方案。

Never trust the data coming from client.永远不要相信来自客户端的数据。 Always validate the posted data before doing any updates/transaction on your database.在对数据库进行任何更新/交易之前,始终验证发布的数据。

So in your HttpPost action method, You run a sanitization check before proceeding因此,在您的 HttpPost 操作方法中,您在继续之前运行清理检查

[HttpPost]
public ActionResult Book(BookingViewModel model)
{
  var isGood = hotelService.DoesCurrentUserHasAccessToRoom(model.RoomId);
  if(isGood)
  {
     //Continue with Saving
     hotelService.BookRoom(model);
     return RedirectToAction("BookingCompleted");
  }
  return View("NotAuthorized");
}

There won't be duplicate code as long as you put them into small reusable methods in your service as needed.只要您根据需要将它们放入服务中的小型可重用方法中,就不会有重复的代码。

Other option would be to add HMAC field to check sensitive data (hidden id field) has not been changed, I use it once and never use it again, overkill solution.其他选项是添加 HMAC 字段来检查敏感数据(隐藏 id 字段)是否未更改,我使用过一次就再也没有使用过,过度解决方案。

That being said...话虽如此...

I have run into the same issue multiple times, and I thought almost the same options, in the long run I stick with simplest solution the GetById repository method passing the CurrentUserId , something like:我多次遇到相同的问题,我想过几乎相同的选项,从长远来看,我坚持使用最简单的解决方案 GetById 存储库方法传递CurrentUserId ,例如:

Entity GetById(int id, int? userId);

Why?为什么?

You are asking a granular level of authorization, which can be obtained only when you know the exact record involved in the operation, so why not check for that in the method responsible for obtaining the record in the first place?您要求的是粒度级别的授权,只有在您知道操作中涉及的确切记录时才能获得,那么为什么不首先在负责获取记录的方法中进行检查呢?

If the record exist and the user created that record then return the record, else return null as the object does not exist for that user, don't even bother returning a "You don't own that object" type of message just a plain and simple 404 "the record does not exist".如果记录存在并且用户创建了该记录,则返回该记录,否则返回 null,因为该用户不存在该对象,甚至不必费心返回“您不拥有该对象”类型的消息,只是一个普通的和简单的 404“记录不存在”。

If you want you can try this same approach but with ActionFilters , something similar to this .如果您愿意,您可以尝试使用相同的方法,但使用ActionFilters ,类似于 But you must figure out how to retrieve the record in that filtercontext.但是您必须弄清楚如何在该过滤器上下文中检索记录。

For admins with full access you can just bypass that check if you pass null to that method.对于具有完全访问权限的管理员,如果您将 null 传递给该方法,您可以绕过该检查。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM