ASP。NET MVC:数据注释验证足够吗?

时间:2021-06-10 16:38:02

I'm using the Data Annotation validation extensively in ASP.NET MVC 2. This new feature has been a huge time saver, as I'm now able to define both client-side validation and server-side validation in one place. However, while I was doing some detailed testing, I realized that it's quite easy for someone to bypass the server-side validation if I relied on Data Annotation validation alone. For example, if I defined a required field by annotating the property with the [Required] attribute and placed a textbox for that required field in a form, a user could simply remove the textbox from the DOM (which can easily be done through Firebug) and now the Data Annotation validation will not be triggered on that property during ModelBinding inside of a Controller. To ensure that the "required" validation is triggered, I can repeat the validation after ModelBinding happens, but then I'd be repeating my validation logic.

我在ASP中大量使用数据注释验证。净MVC 2。这个新特性节省了大量的时间,因为我现在可以在一个地方同时定义客户端验证和服务器端验证。然而,当我在做一些详细的测试时,我意识到,如果我只依赖于数据注释验证,那么很容易有人绕过服务器端验证。例如,如果我定义了一个必需字段的注释[要求]属性的属性,并将一个文本框需要字段的形式,用户可以简单地从DOM中删除文本框(这可以很容易地通过Firebug)现在数据注释验证期间财产将不会触发ModelBinding内部控制器。为了确保“必需的”验证被触发,我可以在模型绑定发生后重复验证,但是我将重复我的验证逻辑。

What is everyone's recommendation on validation? Is Data Annotation validation enough? Or does the validation need to be repeated to ensure that validations get triggered in all situations?

每个人对验证的建议是什么?数据注释验证足够吗?还是需要重复验证以确保在所有情况下都触发验证?

Follow-up comment: Based on the answers below, it seems that I can't rely on the Model Binder and Data Annotation validation alone. Since we're concluding that additional server-side validation is required, is there an easy way for my Service layer to trigger validation based on what's been defined in the Data Annotations? It seems that this will get us the best of both words...we won't need to repeat the validation code, but we'll still ensure that the validation gets executed even if Model Binder doesn't trigger it.

后续评论:基于下面的答案,我似乎不能仅仅依靠模型绑定器和数据注释验证。既然我们得出的结论是需要额外的服务器端验证,那么我的服务层是否有一种简单的方法可以基于数据注释中定义的内容来触发验证?看来这两个词对我们都有好处……我们不需要重复验证代码,但是我们仍然要确保执行验证,即使模型绑定器没有触发它。

I'm going to post this follow-up comment as a separate question, as it poses a different question than the original one.

我将把这个后续评论作为一个单独的问题发表,因为它提出了一个不同于最初的问题。

5 个解决方案

#1


18  

I think to be vigilant concerning security you should choose to you make server validation the priority and ensure that this is always your fallback. Your server validation should work without the client validation. Client validation is more for UX and tho that is paramount to your design, it is secondary to security. With this in mind you will find yourself repeating your validation. A goal is often trying to design your app so that the server and client validation can be integrated as much as possible to reduce the work required to validate on the server and the client. But be assured you must do both.

我认为要警惕安全,你应该选择让服务器验证优先级并确保这总是你的撤退。您的服务器验证应该在没有客户端验证的情况下工作。客户端验证更多的是为了用户体验和tho,这对您的设计来说是至关重要的,它是安全性的次要问题。记住这一点,你会发现自己在重复你的验证。目标通常是设计应用程序,以便尽可能集成服务器和客户端验证,以减少在服务器和客户端进行验证所需的工作。但请放心,你必须同时做这两件事。

If bypassing the client validation (by means of DOM manipulation) is avoiding the server validation (which it seems you are indicating) then your server validation for this instance may not be employed appropriately. You should be invoking your server validation again in your controller action or in a service layer. The scenario you describe should not be defeating your server validation.

如果绕过客户端验证(通过DOM操作)是避免服务器验证(您似乎在表明这一点),那么您对这个实例的服务器验证可能不会被适当地使用。您应该在控制器操作或服务层中再次调用服务器验证。您描述的场景不应该破坏服务器验证。

With the scenario you describe, the DataAnnotation attributes method should be sufficient. It seems that you simply need to make a few code changes to ensure that your server validation is invoked also when submitting the form.

对于您描述的场景,DataAnnotation属性方法应该足够了。您似乎只需要做一些代码更改,以确保在提交表单时也调用服务器验证。

#2


7  

I paired xVal with DataAnnotations and have written my own Action filter that checks any Entity type parameters for validation purposes. So if some field is missing in the postback, this validator will fill ModelState dictionary hence having model invalid.

我将xVal与dataannotation进行了配对,并编写了自己的操作过滤器,用于检查任何实体类型参数以进行验证。因此,如果回发中缺少某个字段,这个验证器将填充ModelState字典,因此模型无效。

Prerequisites:

先决条件:

  • my entity/model objects all implement IObjectValidator interface which declares Validate() method.
  • 我的实体/模型对象都实现了IObjectValidator接口,该接口声明Validate()方法。
  • my attribute class is called ValidateBusinessObjectAttribute
  • 我的属性类被称为ValidateBusinessObjectAttribute
  • xVal validation library
  • xVal验证库

Action filter code:

行动过滤器代码:

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);
            }
        }
    }
}

My controller action is defined like this then:

我的控制器动作是这样定义的:

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

#3


2  

The DataAnnotation is certainly not enough. I use it extensively also to pre-validate my calls to the domain model to get better error reporting and fail as early as possible.

数据注释当然是不够的。我还广泛地使用它来预验证对域模型的调用,以获得更好的错误报告,并尽可能早地失败。

You can however tweak the DataAnnotation Model yourself to ensure properties with [Required] MUST be posted. (will follow up with code later today).

不过,您可以自己调整DataAnnotation模型,以确保必须发布带有[Required]的属性。(今天晚些时候将继续编写代码)。

UPDATE Get the source for DataAnnotations Model Binder and find this line in DataAnnotationsModelBinder.cs

获取dataannotation模型绑定器的源代码,并在DataAnnotationsModelBinder.cs中找到这一行

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

Change it to

将其更改为

// 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


2  

I wrote my own ValidationService for MVC 1.0 by copying patterns from both xVal's DataAnnotationsRuleProvider and Microsoft's DataAnnotationsModelBinder (and Martijn's comments). The service interface is below:

我通过从xVal的DataAnnotationsRuleProvider和微软的DataAnnotationsModelBinder(以及Martijn的评论)复制模式为MVC 1.0编写了自己的验证服务。服务界面如下:

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);
}

The service is a validation runner that walks the property tree of the object instance it receives and actually executes the validation attributes that it finds on each property, building a list of ErrorInfo objects when attributes are not valid. (I'd post the whole source but it was written for a client and I don't know yet if I'm authorized to do so.)

服务是一个验证运行器,它遍历它接收到的对象实例的属性树,并实际执行它在每个属性上找到的验证属性,在属性无效时构建ErrorInfo对象列表。(我会把所有的资料都贴出来,但这是写给一个客户的,我还不知道我是否有权这么做。)

You can then have your controllers, business logic services explicitly invoke validation when you are ready, rather than relying exclusively on the model binder for validation.

然后,您可以让控制器、业务逻辑服务在您准备就绪时显式地调用验证,而不是仅依赖模型绑定器进行验证。

There are a couple of other pitfalls that you should be aware of:

你还需要注意一些其他的陷阱:

  • The default DataTypeAttribute in data annotations doesn't actually do any data type validation, so you'll need to write a new attribute that actually uses xVal regular expressions (or something else) to perform server-side data type validation.
  • 数据注释中的默认DataTypeAttribute实际上不执行任何数据类型验证,因此需要编写一个新的属性,该属性实际上使用xVal正则表达式(或其他东西)来执行服务器端数据类型验证。
  • xVal doesn't walk properties to create client-side validation, so you may want to make some changes there to get more robust client-side validation.
  • xVal没有遍历属性来创建客户端验证,所以您可能需要在那里做一些更改,以获得更健壮的客户端验证。

If I am allowed and have time, I will try to make more source available...

如果我被允许并且有时间,我将尝试提供更多的资源。

#5


1  

See codeProject Server-side Input Validation using Data Annotations

请参阅使用数据注释的codeProject服务器端输入验证

Input validation can be done automatically on the client side in ASP.NET MVC or explicitly validating the model against the rules. This tip will describe how it can be done manually on the server-side of an ASP.NET applications or within the repository code of WPF applications.

在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);

#1


18  

I think to be vigilant concerning security you should choose to you make server validation the priority and ensure that this is always your fallback. Your server validation should work without the client validation. Client validation is more for UX and tho that is paramount to your design, it is secondary to security. With this in mind you will find yourself repeating your validation. A goal is often trying to design your app so that the server and client validation can be integrated as much as possible to reduce the work required to validate on the server and the client. But be assured you must do both.

我认为要警惕安全,你应该选择让服务器验证优先级并确保这总是你的撤退。您的服务器验证应该在没有客户端验证的情况下工作。客户端验证更多的是为了用户体验和tho,这对您的设计来说是至关重要的,它是安全性的次要问题。记住这一点,你会发现自己在重复你的验证。目标通常是设计应用程序,以便尽可能集成服务器和客户端验证,以减少在服务器和客户端进行验证所需的工作。但请放心,你必须同时做这两件事。

If bypassing the client validation (by means of DOM manipulation) is avoiding the server validation (which it seems you are indicating) then your server validation for this instance may not be employed appropriately. You should be invoking your server validation again in your controller action or in a service layer. The scenario you describe should not be defeating your server validation.

如果绕过客户端验证(通过DOM操作)是避免服务器验证(您似乎在表明这一点),那么您对这个实例的服务器验证可能不会被适当地使用。您应该在控制器操作或服务层中再次调用服务器验证。您描述的场景不应该破坏服务器验证。

With the scenario you describe, the DataAnnotation attributes method should be sufficient. It seems that you simply need to make a few code changes to ensure that your server validation is invoked also when submitting the form.

对于您描述的场景,DataAnnotation属性方法应该足够了。您似乎只需要做一些代码更改,以确保在提交表单时也调用服务器验证。

#2


7  

I paired xVal with DataAnnotations and have written my own Action filter that checks any Entity type parameters for validation purposes. So if some field is missing in the postback, this validator will fill ModelState dictionary hence having model invalid.

我将xVal与dataannotation进行了配对,并编写了自己的操作过滤器,用于检查任何实体类型参数以进行验证。因此,如果回发中缺少某个字段,这个验证器将填充ModelState字典,因此模型无效。

Prerequisites:

先决条件:

  • my entity/model objects all implement IObjectValidator interface which declares Validate() method.
  • 我的实体/模型对象都实现了IObjectValidator接口,该接口声明Validate()方法。
  • my attribute class is called ValidateBusinessObjectAttribute
  • 我的属性类被称为ValidateBusinessObjectAttribute
  • xVal validation library
  • xVal验证库

Action filter code:

行动过滤器代码:

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);
            }
        }
    }
}

My controller action is defined like this then:

我的控制器动作是这样定义的:

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

#3


2  

The DataAnnotation is certainly not enough. I use it extensively also to pre-validate my calls to the domain model to get better error reporting and fail as early as possible.

数据注释当然是不够的。我还广泛地使用它来预验证对域模型的调用,以获得更好的错误报告,并尽可能早地失败。

You can however tweak the DataAnnotation Model yourself to ensure properties with [Required] MUST be posted. (will follow up with code later today).

不过,您可以自己调整DataAnnotation模型,以确保必须发布带有[Required]的属性。(今天晚些时候将继续编写代码)。

UPDATE Get the source for DataAnnotations Model Binder and find this line in DataAnnotationsModelBinder.cs

获取dataannotation模型绑定器的源代码,并在DataAnnotationsModelBinder.cs中找到这一行

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

Change it to

将其更改为

// 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


2  

I wrote my own ValidationService for MVC 1.0 by copying patterns from both xVal's DataAnnotationsRuleProvider and Microsoft's DataAnnotationsModelBinder (and Martijn's comments). The service interface is below:

我通过从xVal的DataAnnotationsRuleProvider和微软的DataAnnotationsModelBinder(以及Martijn的评论)复制模式为MVC 1.0编写了自己的验证服务。服务界面如下:

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);
}

The service is a validation runner that walks the property tree of the object instance it receives and actually executes the validation attributes that it finds on each property, building a list of ErrorInfo objects when attributes are not valid. (I'd post the whole source but it was written for a client and I don't know yet if I'm authorized to do so.)

服务是一个验证运行器,它遍历它接收到的对象实例的属性树,并实际执行它在每个属性上找到的验证属性,在属性无效时构建ErrorInfo对象列表。(我会把所有的资料都贴出来,但这是写给一个客户的,我还不知道我是否有权这么做。)

You can then have your controllers, business logic services explicitly invoke validation when you are ready, rather than relying exclusively on the model binder for validation.

然后,您可以让控制器、业务逻辑服务在您准备就绪时显式地调用验证,而不是仅依赖模型绑定器进行验证。

There are a couple of other pitfalls that you should be aware of:

你还需要注意一些其他的陷阱:

  • The default DataTypeAttribute in data annotations doesn't actually do any data type validation, so you'll need to write a new attribute that actually uses xVal regular expressions (or something else) to perform server-side data type validation.
  • 数据注释中的默认DataTypeAttribute实际上不执行任何数据类型验证,因此需要编写一个新的属性,该属性实际上使用xVal正则表达式(或其他东西)来执行服务器端数据类型验证。
  • xVal doesn't walk properties to create client-side validation, so you may want to make some changes there to get more robust client-side validation.
  • xVal没有遍历属性来创建客户端验证,所以您可能需要在那里做一些更改,以获得更健壮的客户端验证。

If I am allowed and have time, I will try to make more source available...

如果我被允许并且有时间,我将尝试提供更多的资源。

#5


1  

See codeProject Server-side Input Validation using Data Annotations

请参阅使用数据注释的codeProject服务器端输入验证

Input validation can be done automatically on the client side in ASP.NET MVC or explicitly validating the model against the rules. This tip will describe how it can be done manually on the server-side of an ASP.NET applications or within the repository code of WPF applications.

在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);