В поисках элегантного и безопасного способа обрезки безопасности "редактировать" представления
Ищите чистый, безопасный способ, позволяющий только определенным пользователям (по ролям) редактировать определенные поля в представлении. Ключевое слово здесь - " редактировать", потому что одно дело просто показать / скрыть части представления (кнопки, ссылки и т. Д.), Но другое - как обрабатывать "защищенные" поля, когда они отправляются обратно в контроллер.
Например, я мог бы сделать это:
Посмотреть модель
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()
);
}