Validation should, of course, protect your entire application.
However, it is often the case that you need to apply validation in
more than one location. If your application consists of layers,
distributed services, or discrete components, you probably need to
validate at each boundary. This is especially the case where
individual parts of the application could be called from more than one
place (for example, a business layer that is used by several user
interfaces and other services).
It is also a really good idea to validate at trust boundaries,
even if the components on each side of the boundary are not physically
separated. For example, your business layer may run under a different
trust level or account context than your data layer (even if they
reside on the same machine). Validation at this boundary can prevent
code that is running in low trust and which may have been compromised,
from submitting invalid data to code that runs in higher trust
mode.
Finally, a common scenario: validation in the user interface.
Validating data on the client can improve application responsiveness,
especially if the UI is remote from the server. Users do not have to
wait for the server to respond when they enter or submit invalid data,
and the server does not need to attempt to process data that it will
later reject. However, remember that even if you do validate data on
the client or in the UI you must always revalidate on the server or in
the receiving service. This protects against malicious users who may
circumvent client-side validation and submit invalid data.
To put it simply, everything. Or, at least any input values you
will use in your application that may cause an error, involve a
security risk, or could result in incorrect processing. Remember that
Web page and service requests may contain data that the user did not
enter directly, but could be used in your application. This can
include cookies, header information, credentials, and context
information that the server may use in various ways. Treat all input
data as suspicious until you have validated it.
For maximum security, your validation process should be designed to accept only
data that you can directly determine to be valid. This approach is
known as positive validation and generally uses an allow list that
specifies data that satisfies defined criteria, and rejects all other
data. Examples are rules that check if a number is between two
predefined limits, or if the submitted value is within a list of valid
values. Use this approach whenever possible.
The alternative and less-secure approach is to use a block list
containing values that are not valid. This is called negative
validation, and generally involves accepting only data that does not
meet specific criteria. For example, as long as a string does not
contain any of the specified invalid characters, it would be accepted.
You should use this approach cautiously and as a secondary line of
defense, because it is very difficult to create a complete list of
criteria for all known invalid input—which may allow malicious data to
enter your system.
Finally, consider sanitizing data. While this is not strictly a
validation task, you can as an extra precaution attempt to eliminate
or translate characters in an effort to make the input safe. However,
do not rely on this technique alone because, as with negative
validation, it can be difficult to create a complete list of criteria
for all known invalid input unless there is a limited range of invalid
values.
2. What Does the Validation Block Do?
The Validation block consists of a broad range of validators, plus
a mechanism that executes these validators and collects and correlates
the results to provide an overall validation result (true/valid or
false/invalid). The Validation block can use individual attributes
applied to classes and class members that the application uses (both the
validation attributes provided with the Validation block and data
annotation attributes from the System.ComponentModel.DataAnnotations namespace),
in addition to rule sets defined in the configuration of the block,
which specify the validation rules to apply.
The typical scenario when using the Validation block is to define
rule sets through configuration or attributes applied to your classes.
Each rule set specifies the set of individual validators and
combinations of these validators that implement the validation rules you
wish to apply to that class. Then you use a ValidatorFactory (or one of the equivalent
implementations of this factory) to create a type validator for the
class, optionally specifying the rule set it should use. If you don't
specify a rule set, it uses the default rules. Then you can call the
Validate method of the type validator.
This method returns an instance of the ValidationResults class that contains details of
all the validation errors detected. Figure 1 illustrates this
process.
When you use a rule set to validate an instance of a specific type
or object, the block can apply the rules to:
-
The type itself
-
The values of public readable properties
-
The values of public fields
-
The return values of public methods that take no
parameters
Note
Notice that you can validate the values of method
parameters and the return type of methods that take parameters when
that method is invoked, only by using the validation call handler
(which is part of the Validation block) in conjunction with the Unity
dependency injection and interception mechanism. The validation call
handler will validate the parameter values based on the rules for each
parameter type and any validation attributes applied to the
parameters. We don't cover the use of the validation call handler in
this guide, as it requires you to be familiar with Unity interception
techniques. For more information about interception and the validation
call handler, see the Unity interception documentation installed with
Enterprise Library or available online at http://go.microsoft.com/fwlink/?LinkId=188875.
Alternatively, you can create individual validators
programmatically to validate specific values, such as strings or numeric
values. However, this is not the main focus of the block.
In addition, the Validation block contains features that integrate
with Windows® Forms, Windows Presentation Foundation (WPF), ASP.NET, and
Windows Communication Foundation (WCF) applications. These features use
a range of different techniques to connect to the UI, such as a proxy
validator class based on the standard ASP.NET Validator control that you can add to a Web page,
a ValidationProvider class that you can
specify in the properties of Windows Forms controls, a ValidatorRule class that you can specify in the
definition of WPF controls, and a behavior extension that you can
specify in the <system.ServiceModel> section of your WCF
configuration.
2.1 The Range of Validators
Validators implement functionality for validating Microsoft®
.NET Framework data types. The validators included with the Validation
block fall into three broad categories: value validators, composite
validators, and type (object) validators. The value validators allow
you to perform specific validation tests such as verifying:
-
The length of a string, or the occurrence of a specified set
of characters within it.
-
Whether a value lies within a specified range, including
tests for dates and times relative to a specified
date/time.
-
Whether a value is one of a specified set of values, or can
be converted to a specific data type or enumeration value.
-
Whether a value is null, or is the same as the value of a
specific property of an object.
-
Whether the value matches a specified regular
expression.
The composite validators are used to combine other validators
when you need to apply more complex validation rules. The Validation
block includes an ANDOR validator, each of which acts as a
container for other validators. By nesting these composite validators
in any combination and populating them with other validators, you can
create very comprehensive and very specific validation rules. validator and an
Table 1
describes the complete set of validators provided with the Validation block.
Table 1. The validators provided with the Validation block
Validator type |
Validator name |
Description |
---|
Value Validators
|
Contains Characters Validator |
Checks that an arbitrary string, such as a string
entered by a user in a Web form, contains any or all of the
specified characters. |
Date Time Range Validator |
Checks that a DateTime object falls within a
specified range. |
Domain Validator |
Checks that a value is one of the specified
values in a specified set. |
Enum Conversion Validator |
Checks that a string can be converted to a value
in a specified enumeration type. |
Not Null Validator |
Checks that the value is not null. |
Property Comparison Validator |
Compares the value to be checked with the value
of a specified property. |
Range Validator |
Checks that a value falls within a specified
range. |
Regular Expression Validator |
Checks that the value matches the pattern
specified by a regular expression. |
Relative Date Time Validator |
Checks that the DateTime value falls within a
specified range using relative times and dates. |
String Length Validator |
Checks that the length of the string is within
the specified range. |
Type Conversion Validator |
Checks that a string can be converted to a
specific type. |
Type Validators
|
Object Validator |
Causes validation to occur on an object
reference. All validators defined for the object's type will
be invoked. |
Object Collection Validator |
Checks that the object is a collection of the
specified type and then invokes validation on each element of
the collection. |
Composite Validators
|
And Composite Validator |
Requires all validators that make up the
composite validator to be true. |
Or Composite Validator |
Requires at least one of the validators that make
up the composite validator be true. |
Single Member Validators
|
Field Value Validator |
Validates a field of a type. |
Method Return Value Validator |
Validates the return value of a method of a
type. |
Property Value Validator |
Validates the value of a property of a
type. |