ASP.NET MVC:数据注释验证就足够了吗?

发布于 2024-08-08 01:10:11 字数 612 浏览 3 评论 0原文

我在 ASP.NET MVC 2 中广泛使用数据注释验证。这一新功能节省了大量时间,因为我现在能够在一个地方定义客户端验证和服务器端验证。然而,当我进行一些详细测试时,我意识到如果我单独依赖数据注释验证,那么有人很容易绕过服务器端验证。例如,如果我通过使用 [Required] 属性注释属性来定义必填字段,并在表单中为该必填字段放置一个文本框,则用户可以简单地从 DOM 中删除该文本框(这可以通过 Firebug 轻松完成)现在,在控制器内部的模型绑定期间,不会在该属性上触发数据注释验证。为了确保触发“必需”验证,我可以在 ModelBinding 发生后重复验证,但随后我将重复我的验证逻辑。

大家对验证有何建议?数据注释验证就足够了吗?或者是否需要重复验证以确保在所有情况下都触发验证?

后续评论: 根据下面的答案,我似乎不能单独依赖模型绑定器和数据注释验证。由于我们得出的结论是需要额外的服务器端验证,因此我的服务层是否有一种简单的方法可以根据数据注释中定义的内容触发验证?看来这将使我们两全其美......我们不需要重复验证代码,但我们仍然会确保即使 Model Binder 不触发验证也会执行它。

我将将此后续评论作为一个单独的问题发布,因为它提出了与原始问题不同的问题。

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.

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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

半窗疏影 2024-08-15 01:10:11

我认为要对安全性保持警惕,您应该选择将服务器验证作为优先事项,并确保这始终是您的后备措施。您的服务器验证应该在没有客户端验证的情况下工作。客户端验证更多的是针对用户体验,尽管这对您的设计至关重要,但它对于安全性来说是次要的。考虑到这一点,您会发现自己在重复验证。一个目标通常是尝试设计您的应用程序,以便尽可能集成服务器和客户端验证,以减少在服务器和客户端上验证所需的工作。但请放心,您必须两者都做。

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

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

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.

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.

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.

圈圈圆圆圈圈 2024-08-15 01:10:11

我将 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();
    }
    ...
}

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.

Prerequisites:

  • my entity/model objects all implement IObjectValidator interface which declares Validate() method.
  • my attribute class is called ValidateBusinessObjectAttribute
  • xVal validation library

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();
    }
    ...
}
别念他 2024-08-15 01:10:11

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

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

更新
获取 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)) {

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).

UPDATE
Get the source for DataAnnotations Model Binder and find this line in 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)) {
虐人心 2024-08-15 01:10:11

我通过复制 xVal 的 DataAnnotationsRuleProvider 和 Microsoft 的 DataAnnotationsModelBinder(以及 Martijn 的评论)中的模式,为 MVC 1.0 编写了自己的 ValidationService。服务接口如下:

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 不会走路
    用于创建客户端的属性
    验证,所以你可能想要
    进行一些更改以变得更加强大
    客户端验证。

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

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:

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.)

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.
  • 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.

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

独留℉清风醉 2024-08-15 01:10:11

请参阅 codeProject 使用数据注释进行服务器端输入验证< /a>

输入验证可以在客户端自动完成
ASP.NET MVC 或根据规则显式验证模型。这
提示将描述如何在服务器端手动完成
ASP.NET 应用程序或 WPF 存储库代码中
应用程序。

 // 使用 ValidationContext 根据产品数据注释验证产品模型
        // 保存到数据库之前
        var validationContext = new ValidationContext(productViewModel, serviceProvider: null, items:null);
        var validationResults = new List();

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

See codeProject Server-side Input Validation using Data Annotations

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.

        // 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);
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文