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

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

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

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

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

5 ответов

Решение

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

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

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

Я связал xVal с DataAnnotations и написал свой собственный фильтр действий, который проверяет любые параметры типа сущностей в целях проверки. Таким образом, если какое-либо поле отсутствует в обратной передаче, этот валидатор заполнит словарь 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();
    }
    ...
}

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

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

ОБНОВЛЕНИЕ Получите источник для связывателя модели DataAnnotations и найдите эту строку в DataAnnotations Model Binder.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)) {

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

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 (или что-то еще) для выполнения проверки типа данных на стороне сервера.
  • xVal не использует свойства для создания проверки на стороне клиента, поэтому вы можете внести некоторые изменения, чтобы получить более надежную проверку на стороне клиента.

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

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