В поисках элегантного и безопасного способа обрезки безопасности "редактировать" представления

Ищите чистый, безопасный способ, позволяющий только определенным пользователям (по ролям) редактировать определенные поля в представлении. Ключевое слово здесь - " редактировать", потому что одно дело просто показать / скрыть части представления (кнопки, ссылки и т. Д.), Но другое - как обрабатывать "защищенные" поля, когда они отправляются обратно в контроллер.

Например, я мог бы сделать это:

Посмотреть модель

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
}

Но проблема в том, что когда пользователь без прав администратора редактирует клиента, поля Email а также Phone никогда не отображаются в представлении, поэтому они будут иметь значение 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)
}

Это сохранит исходные значения Email/Phone, даже когда пользователь, не являющийся администратором, редактирует запись, но тогда проблема заключается в том, что поля Email/Phone доступны в отображаемом HTML как скрытые поля и могут легко управляться инструментом браузера до отправлять обратно.

У меня есть некоторые идеи, но они становятся грязными. Поэтому я хотел бы знать, какие успешные подходы уже могут быть для чего-то подобного.

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()
                );
} 
Другие вопросы по тегам