【问题标题】:Looking for elegant and secure way for security-trimming "edit" views为安全修剪“编辑”视图寻找优雅和安全的方式
【发布时间】:2018-01-10 17:13:41
【问题描述】:

寻找一种干净、安全的方式,只允许某些用户(按角色)编辑视图中的某些字段。这里的关键词是edit,因为只显示/隐藏视图的一部分(按钮、链接等)是一回事,而如何处理“受保护”字段则是另一回事被发布回控制器。

例如,我可以这样做:

查看模型

public class CustomerEditViewModel
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    // Other properties...

    public string Email { get; set; }
    public string Phone { get; set; }

    public bool CanEditContactInfo { get; set; } 
}

查看

@Html.EditorFor(m => m.FirstName)
@Html.EditorFor(m => m.LastName)

@if (Model.CanEditContactInfo)
{
    @Html.EditorFor(m => m.Email)
    @Html.EditorFor(m => m.Phone)
}

控制器

[HttpGet]
public ActionResult Edit(int id)
{
    var customer = _customerService.GetCustomerById(id);
    var model = Mapper.Map<CustomerEditViewModel>(customer);
    model.CanEditContactInfo = User.IsInRole("Admin");
    return View(model);
}

[HttpPost]
public ActionResult Edit(CustomerEditViewModel model)
{
    var customer = _customerRepo.GetCustomerById(model.Id);
    Mapper.Map(model, customer);
    _customerService.UpdateCustomer(customer);
    // return to Index view
}

但问题是,当非管理员用户编辑客户时,EmailPhone 字段永远不会在视图上呈现,因此当表单回发到控制器时它们将为 NULL,并且会进一步用 NULL 覆盖数据库中的实际值。

我可以这样解决:

@Html.EditorFor(m => m.FirstName)
@Html.EditorFor(m => m.LastName)

@if (Model.CanEditContactInfo)
{
    @Html.EditorFor(m => m.Email)
    @Html.EditorFor(m => m.Phone)
}
else
{
    @Html.HiddenFor(m => m.Email)
    @Html.HiddenFor(m => m.Phone)
}

即使非管理员用户正在编辑记录,这也会保留原始电子邮件/电话值,但问题是电子邮件/电话字段在呈现的 HTML 中作为隐藏字段可用,并且可以很容易地被发回之前的浏览器工具。

我确实有一些想法,但它们变得一团糟。所以我想知道对于这样的事情可能已经有哪些成功的方法。

【问题讨论】:

  • 如果用户没有角色,为什么不直接忽略这些字段?
  • 取决于您所说的“忽略”。我已经解释过,如果只是从视图中忽略(即“忽略它们”的一种方式),它们在回发到控制器时将为 NULL,并将这些 NULL 保存到数据库中(覆盖实际的电子邮件/电话值)。除非您所说的“忽略”是指别的意思?
  • 我的意思是无论你在哪里更新实体数据都忽略它们(我猜是UpdateCustomer
  • 我明白了。我认为这是有道理的,但我必须将用户的角色下推到该层(我的“服务”层),以便它可以检查进行更新的人是否是管理员。或者,我可以进行两个单独的服务调用,例如 UpdateCustomerUpdateCustomerWithContactInfo,但不确定这是否正确。
  • 在我看来,您的服务层应该已经强制执行此类权利/权限。

标签: c# asp.net-mvc security razor security-trimming


【解决方案1】:

安全编码的第一条经验法则是客户端输入不可信,这意味着您必须在服务器端应用检查和验证。 在您的情况下, HttpPost 控制器操作应再次检查用户角色。如果用户无权更新所有属性,那么您可以:

1- 再次加载原始数据并覆盖可通过用户输入更新的属性,然后保存此更新后的副本:

[HttpPost]
public ActionResult Edit(CustomerEditViewModel model)
{
    var customer = _customerRepo.GetCustomerById(model.Id);
    // Check the current user role.    
    If (!User.IsInRole("Admin"))
    {
       customer.FirstName = model.FirstName;
       customer.LastName = model.LastName;
    }
    else
    {
       Mapper.Map(model, customer);
    }
    _customerService.UpdateCustomer(customer);
    // return to Index view
}

2-创建另一个映射方法,忽略用户输入无法更新的属性,并根据用户权限调用正确的映射器

public static Customer ToEntity(this CustomerEditViewModel model, Customer destination)
{
    return Mapper.CreateMap<CustomerEditViewModel, Customer>()
                 .ForMember(dest => dest.Email, opt => opt.Ignore()
                 .ForMember(dest => dest.Phone, opt => opt.Ignore()
                );
} 

【讨论】:

  • 我开始像这样下去,但不确定它是否变得太乱了。不确定我是否更喜欢控制器中的额外手动映射,或者 AutoMapper 中的替代映射。我也在考虑两种独立的视图模型(一种用于管理员,一种用于非管理员),但我认为这也有可能变得混乱。
  • 拥有两个独立的视图模型甚至两个独立的视图肯定更安全;但它增加了代码冗余(换句话说,它有一些额外的成本)。最后,您需要在成本和收益之间找到适当的平衡点,并找出适合您的应用程序的安全级别。
猜你喜欢
  • 2021-02-22
  • 1970-01-01
  • 1970-01-01
  • 2012-08-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-09-17
  • 2012-06-05
相关资源
最近更新 更多