Подтвердить что ты не робот

ASP.NET MVC: достаточна ли проверка аннотации данных?

Я широко использую проверку аннотаций данных в ASP.NET MVC 2. Эта новая функция была огромной экономией времени, так как теперь я могу определить как проверку на стороне клиента, так и проверку на стороне сервера в одном месте, Тем не менее, пока я проводил детальное тестирование, я понял, что для кого-то достаточно просто обойти проверку на стороне сервера, если я полагаюсь только на проверку аннотации данных. Например, если я определил требуемое поле, аннотируя свойство с атрибутом [Обязательный] и поместив текстовое поле для этого обязательного поля в форме, пользователь может просто удалить текстовое поле из DOM (которое легко можно сделать через Firebug) и теперь валидация аннотации данных не будет активирована в этом свойстве во время ModelBinding внутри Контроллера. Чтобы убедиться, что "требуемая" проверка активирована, я могу повторить проверку после ModelBinding, но тогда я бы повторил логику проверки.

Какова рекомендация по проверке? Является ли проверка аннотации данных достаточной? Или нужно повторить проверку, чтобы убедиться, что проверки активируются во всех ситуациях?

Последующий комментарий: Основываясь на ответах ниже, кажется, что я не могу полагаться только на валидацию модели Binder и Data Annotation. Поскольку мы заключаем, что требуется дополнительная проверка на стороне сервера, существует ли простой способ для моего уровня сервиса инициировать проверку на основе того, что было определено в аннотации данных? Похоже, что это даст нам лучшее из обоих слов... нам не нужно будет повторять код проверки, но мы по-прежнему будем гарантировать, что проверка будет выполнена, даже если Model Binder не вызывает ее.

Я собираюсь опубликовать этот комментарий в виде отдельного вопроса, поскольку он задает другой вопрос, чем оригинальный.

4b9b3361

Ответ 1

Я думаю, что для бдительности в отношении безопасности вы должны выбрать, чтобы вы сделали валидацию сервера приоритетом и убедитесь, что это всегда ваш резерв. Проверка вашего сервера должна работать без проверки клиента. Валидация клиента больше для UX, и это имеет первостепенное значение для вашего дизайна, это вторично для безопасности. Имея это в виду, вы обнаружите, что повторяете свою проверку. Цель часто заключается в том, чтобы спроектировать ваше приложение, чтобы можно было максимально интегрировать проверку сервера и клиента, чтобы уменьшить работу, требуемую для проверки на сервере и клиенте. Но будьте уверены, вы должны сделать то и другое.

Если обход проверки клиента (посредством манипуляции с DOM) позволяет избежать проверки сервера (что, кажется, вы указываете), то проверка вашего сервера для этого экземпляра может быть неправильно использована. Вы должны снова активировать проверку своего сервера в действии вашего контроллера или на уровне обслуживания. Сценарий, который вы описываете, не должен побеждать проверку вашего сервера.

При описанном сценарии метод атрибутов DataAnnotation должен быть достаточным. Похоже, вам просто нужно внести несколько изменений кода, чтобы убедиться, что ваша проверка сервера вызывается также при отправке формы.

Ответ 2

Я связал xVal с DataAnnotations и написал свой собственный фильтр действий, который проверяет все параметры типа Entity для целей проверки. Поэтому, если в обратной передаче отсутствует какое-либо поле, этот валидатор заполнит словарь ModelState, следовательно, недействителен модель.

Предпосылки:

  • объекты объекта/модели объекта реализуют интерфейс IObjectValidator, который объявляет метод Validate().
  • мой класс атрибутов называется ValidateBusinessObjectAttribute
  • библиотека проверки достоверности xVal

Код фильтра действий:

public void OnActionExecuting(ActionExecutingContext filterContext)
{
    IEnumerable<KeyValuePair<string, object>> parameters = filterContext.ActionParameters.Where<KeyValuePair<string, object>>(p => p.Value.GetType().Equals(this.ObjectType ?? p.Value.GetType()) && p.Value is IObjectValidator);
    foreach (KeyValuePair<string, object> param in parameters)
    {
        object value;
        if ((value = param.Value) != null)
        {
            IEnumerable<ErrorInfo> errors = ((IObjectValidator)value).Validate();
            if (errors.Any())
            {
                new RulesException(errors).AddModelStateErrors(filterContext.Controller.ViewData.ModelState, param.Key);
            }
        }
    }
}

Мое действие контроллера определяется следующим образом:

[ValidateBusinessObject]
public ActionResult Register(User user, Company company, RegistrationData registrationData)
{
    if (!this.ModelState.IsValid)
    {
        return View();
    }
    ...
}

Ответ 3

DataAnnotation, конечно, недостаточно. Я также широко использую его, чтобы предварительно проверить мои вызовы на модель домена, чтобы получить лучшее сообщение об ошибках и как можно раньше.

Однако вы можете настроить модель DataAnnotation самостоятельно, чтобы убедиться, что свойства с [Обязательно] ДОЛЖНЫ быть отправлены. (теперь будет следить за кодом позже).

UPDATE Получите источник для DataAnnotations Model Binder и найдите эту строку в DataAnnotationsModelBinder.cs

// Only bind properties that are part of the request
if (bindingContext.ValueProvider.DoesAnyKeyHavePrefix(fullPropertyKey)) {

Измените его на

// Only bind properties that are part of the request
bool contextHasKey = bindingContext.ValueProvider.DoesAnyKeyHavePrefix(fullPropertyKey);
bool isRequired = GetValidationAttributes(propertyDescriptor).OfType<RequiredAttribute>().Count() > 0;
if (contextHasKey || (!contextHasKey && isRequired)) {

Ответ 4

Я написал свой собственный ValidationService для MVC 1.0, скопировав шаблоны из xVal DataAnnotationsRuleProvider и Microsoft DataAnnotationsModelBinder (и комментарии Martijn). Сервисный интерфейс ниже:

public interface IValidationService
{
    void Validate(object instance);

    IEnumerable<ErrorInfo> GetErrors(object instance);
}

public abstract class BaseValidationService : IValidationService
{
    public void Validate(object instance)
    {
        var errors = GetErrors(instance);

        if (errors.Any())
            throw new RulesException(errors);
    }

    public abstract IEnumerable<ErrorInfo> GetErrors(object instance);
}

Служба - это проводник проверки, который обрабатывает дерево свойств экземпляра объекта, которое он получает, и фактически выполняет атрибуты проверки, которые он находит для каждого свойства, создавая список объектов ErrorInfo, когда атрибуты недействительны. (Я бы опубликовал весь источник, но он был написан для клиента, и я пока не знаю, разрешено ли мне это делать.)

Затем вы можете использовать свои контроллеры, службы бизнес-логики, которые явно вызывают проверку, когда вы готовы, а не полагаться исключительно на привязку модели для проверки.

Есть несколько других подводных камней, о которых вы должны знать:

  • Значение DataTypeAttribute по умолчанию аннотации фактически не делают проверка типа данных, поэтому вам понадобится написать новый атрибут, который на самом деле использует xVal regular выражения (или что-то еще) выполнять тип данных на стороне сервера Проверка.
  • xVal не идет свойства для создания клиентской стороны проверки, поэтому вы можете захотеть сделать некоторые изменения там, чтобы получить более надежные проверка на стороне клиента.

Если мне позволено и у меня будет время, я постараюсь сделать доступным больше источников...

Ответ 5

См. codeProject Подтверждение ввода на стороне сервера с использованием аннотаций данных

Проверка ввода может выполняться автоматически на стороне клиента в ASP.NET MVC или явно проверяя модель на соответствие правилам. Эта совет будет описывать, как это можно сделать вручную на стороне сервера Приложения ASP.NET или в коде репозитория WPF приложения.

        // Use the ValidationContext to validate the Product model against the product data annotations
        // before saving it to the database
        var validationContext = new ValidationContext(productViewModel, serviceProvider: null, items:null);
        var validationResults = new List<ValidationResult>();

        var isValid = Validator.TryValidateObject(productViewModel, validationContext,validationResults, true);