寻找优雅安全的安全方式 - 修剪“编辑”视图

时间:2018-01-10 17:13:41

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

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

例如,我可以这样做:

查看模型

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中可用作隐藏字段,并且很容易被操作回帖之前的浏览器工具。

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

1 个答案:

答案 0 :(得分:2)

安全编码的第一个经验法则是客户端输入不可信,这意味着您必须在服务器端应用检查和验证。 在您的情况下,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()
                );
}