Site Meter
 
 

Monthly Archives: September 2008

Thoughts on validation in ASP.NET MVC applications

A couple of months back, I supplied some code for doing model-based validation in an ASP.NET MVC application in a way that automatically generates client-side validation JavaScript. That was pretty popular. Lots of people are enthusiastic about expressing validation rules as attributes on model properties. However, ASP.NET MVC has been enhanced since then, and so have my views on what constitutes tidy and effective validation.

So, let’s start with the new technology. MVC Preview 5 comes with two new API pieces that seem relevant to validation:

  • ModelState. Controllers now have an official way to pass validation error information to views. Populate ViewData.ModelState, then you can use the built-in HTML helpers to render error message summaries, and to highlight input fields that correspond to errors.
  • Model binders. Controllers are expected to populate model objects from form posts using the IModelBinder API. You can create custom binders – the framework calls your binder to obtain a value for each model property. (If you don’t understand this, don’t worry – it turns out not to be relevant.)

The obvious thing to do (and I’ve seen several forum posts and blog comments suggest this) is to make a custom model binder that applies validation rules (maybe ones expressed as attributes on model properties, such as [IsRequired]) and populates ModelState with any errors it finds. However, there are technical issues that make such a design unworkable in Preview 5 (model binders don’t get told what type of model object they’re providing values for). And more importantly than the current technical issues, that sort of design would be totally undesirable anyway. Validation has nothing to do with controllers binding form posts to model objects, as I’ll explain.

In the last week, Scott Guthrie posted about handling form posting/validation scenarios in ASP.NET MVC, and so did Stephen Walther. The following design has some things in common with each of their designs, but some important differences too.

Basic principles

So, how do you design a pattern for validation? Like any design, start by deciding what characteristics are really important to you, and build from there. Here’s what I consider to be important truths of validation:

  1. Model objects should perform and enforce validation at the appropriate time.
  2. Validation rules should be expressible in plain old C#.
  3. Validation rules and business rules are at opposite ends of the same spectrum.
  4. Errors should bubble up to the UI automatically (no code needed).
  5. Client-side validation is optional – it’s purely for convenience.

And what does that mean?

[1] says that model objects don’t merely hold validation rules (e.g., as attributes on their properties), but model objects also actually enforce those rules. So, it’s not acceptable for a controller class to make the decision to validate (as they do in many other examples) – that would undermine the model’s encapsulation, and would mean that a badly-behaved controller could choose to skip validation. It’s the model that enforces validation, and it does so at a time of its own choosing – usually when something is being committed (e.g., when the model object is being saved to the database), but it might also choose to validate simple property formatting rules during property setters.

Now, if the model is capable of enforcing validation at a time of its own choosing, then it needs a general-purpose mechanism to forcibly abort certain controller operations when validation rules are violated. Fortunately, C# comes with the perfect mechanism: exceptions. There’s nothing clever about this (simplicity is the goal) – when the model decides it won’t allow an operation, it throws an exception. The operation is forcibly aborted. The controller must obey.

Also, bear in mind that certain validation rules can only be enforced at the point of committing data or finalizing actions. For example, “usernames must be unique” will probably be enforced in your database (with a UNIQUE constraint). You therefore can’t rely on a single “validation” moment – the model has to be free to issue rule exceptions at any point during its processing, and the controller shouldn’t need any special-case logic to deal with this. This requirement fits quite easily into the following design.

[2] just says that I don’t want to rely on any kind of rules engine. Now I’m perfectly happy to have reusable attributes to represent certain simple property formatting rules (e.g., [IsRequired], [DataFormat(Formats.EmailAddress)]), and you could even build a custom rules engine, but these are built on the fact that arbitrary C# is allowed.

[3] tries to clarify that simple property formatting rules (e.g., must be valid SSN) are a good start, but not adequate on their own. Certain business rules describe whether or not an operation is allowed (e.g., usernames must be unique), might relate to interactions of multiple properties, might involve arbitrarily complex logic, and these ought to fit into the validation framework too. See the diagram below.

image_thumb2

[4] is about making the system easy to maintain. When I add a new validation rule to my model, I want all controllers/views that work on that model type to render suitable error messages automatically, without changing any controller/view code.

Now, remember that when the model decides some rule has been violated, it throws an exception. If that exception is of some special type (let’s call it BusinessRuleException), then that exception can describe in some strongly-typed way which rule was validated and how. In fact, it can describe multiple rule validations simultaneously. All we need now is for controllers to catch BusinessRuleExceptions, use them to populate ViewData.ModelState, then ASP.NET MVC’s built-in helpers will take care of displaying the messages. We can do localization in this step too, for multilingual error messages.

[5] says that it’s nice to have a helper to generate client-side validation code automatically, but the helper doesn’t have to replicate every rule on the client. Obviously, since we’re allowing arbitrary C# rules, there’s no automatic translation of arbitrary rules to JavaScript. Sometimes client-side validation is impossible anyway (e.g., “usernames must be unique”). However, if some simple property formatting rules are implemented as attributes (e.g., [IsRequired]), then we can generate client-side code from these attributes. It won’t cover all rules, but it will cover many of them, and that’s good enough.

Time for some code plz

Yes I know – reading English is hard work. Far easier if I show you some C# code. Here’s how a model class might look:

public class Person
{
    [Required] [StringLength(20)]
    public string Name { get; set; }
 
    [Required] [Range(1, 200)]
    public int? Age { get; set; }
 
    public void EnsureValid()
    {
        // If any of this object's property values conflicts with a validation [Attribute],
        // it will add an entry to the violations collection
        RuleViolations violations = ValidationHelpers.RunValidationAttributes(this);
 
        // Now we can run any other custom C# validation logic
        if(Age.HasValue && ((DateTime.Now.Year - Age.Value) % 4 == 0))
            violations.Add(new ArbitraryViolation("Age", "Sorry, you were probably born on a leap year, so we won't let you register."));
 
        if (!violations.IsEmpty)
            throw new BusinessRuleException(violations);
    }
}
 
public static class PersonRepository
{
    public static void SavePerson(Person person)
    {
        // The model is reponsible for deciding when to validation.
        // It chooses to validate now. If there's a violation, it will
        // throw an exception
        person.EnsureValid();
 
        // Good - it was valid. Todo: now save to database
    }
}

As you can see, there are some simple property format rules implemented using attributes (these are the standard ones shipped in System.ComponentModel.DataAnnotations), but these are just a special case. You can of course use arbitrary C# logic when enforcing validation.

I don’t need to have a special method marked as the validation method (or an IValidatable interface or anything like that), because the model layer makes its own decision when to validate, and chooses how to do that. It just has to throw a BusinessRuleException if there’s a problem. In this example, it chooses to enforce validation before writing to the database, but you could choose to validate certain properties during their setter methods if you wanted.

Next, how does a controller look? It follows a general pattern like the following. The critical bit is that model operations are wrapped in a try…catch so that it can recover from errors and display suitable UI feedback.

public class RegistrationController : Controller
{
    [AcceptVerbs("GET")]
    public ViewResult Edit() { return View(new Person()); }
 
    [AcceptVerbs("POST")]
    public ActionResult Edit(string unused)
    {
        Person model = new Person();
        try
        {
            if (!TryUpdateModel(model, new[] { "Name", "Age" })) // At some point it may be possible to have a custom binder that throws a BusinessRuleException if there were setter exceptions, but as of Preview 5, have to use the slightly hacky TryUpdateModel()
                throw new BusinessRuleException();
 
            // The real validation happens to be enforced by the model here, but that's no concern for the controller
            PersonRepository.SavePerson(model);
 
            // The operation completed successfully (so validation must have passed)
            return RedirectToAction("Completed");
        }
        catch (BusinessRuleException ex)
        {
            // Generate suitable localized UI feedback based on exception that was thrown
            ex.AddToModelState(ViewData.ModelState);
            // Re-render edit screen
            return View(model);
        }
    }
}

Notice the call to ex.AddToModelState(ViewData.ModelState);. I’ve implemented that as an extension method on BusinessRuleException (so as not to mix ASP.NET MVC concerns with the core BusinessRuleException class) – it simply iterates through the list of RuleViolations objects, and generates/adds some localized UI feedback to ViewData.ModelState for each violation.

I haven’t included the full source code here because it’s still all a bit proof-of-concept, but it’s not too hard to fill in the gaps and get a working implementation.

image

And what of client-side validation?

As we discussed earlier, not all rules can be translated to client-side validation code. However, since some of the simpler rules (those on the green end of the validation-businessrule spectrum) are often implemented as System.ComponentModel.DataAnnotations attributes, it would be a straightforward job to make a Html.ClientSideValidation(object model) helper that dynamically generates suitable validation JavaScript. This bit is essentially the same as what I implemented in my previous MVC validation post.

Of course, you could have a pluggable system to generate a JS validation configuration suitable for multiple client-side validation frameworks.

About setter exceptions

As mentioned earlier, sometimes it’s desirable to enforce validation rules in property setters. This gives you solid assurance that your model object can’t have invalid property values, because when *any* code (an MVC controller or model binder, or some other domain or service code) tries to manipulate the model object, they’ll never get away with setting invalid property values. This strategy applies only to simple individual property formatting rules (the green end of the spectrum).

When setters throw exceptions, MVC Preview 5′s DefaultModelBinder is clever enough to capture them one property at a time, building up a ModelState structure. Unfortunately, at present, it disregards the exception type, so you can’t use it to generate suitable UI feedback later. I’ve asked for this to be fixed – let’s be optimistic that the MVC team will give tighter support for setter exceptions in the next update.

Now, here’s an interesting twist. If we want some validation rules to be enforced in setters, and those rules are expressed as [Attributes], then wouldn’t it be cool if that logic was automatically injected into the setter by the attribute? This gets us into aspect-oriented programming territory (AOP). Unfortunately the .NET CLR doesn’t have much support for AOP. PostSharp is the best option we’ve got, but it does require a special code compilation process.

If instead of using the System.ComponentModel.DataAnnotations attributes we used custom attributes, then they could implement PostSharp’s special interfaces. Then, when you compile with PostSharp, the attribute’s simple validation logic can automatically get injected into the property setter. However, if you don’t compile with PostSharp, then the logic wouldn’t automatically get injected into the setter, and you’d cleanly fall back on the behavior I showed earlier in this post.

Of course, this would only be useful if ASP.NET MVC gets better support for setter exceptions.

So, would you do validation like this?

Long post… sorry! Dear readers, I’d be very pleased to hear of any feedback on this design. If there’s any consensus on how it should work, then I or someone else could work it up into a downloadable reusable set of components and classes. This would include:

  • The BusinessRuleException class that you can throw from your model layer to signal and describe arbitrary invalid user actions. It would hold a set of strongly-typed RuleViolation classes (e.g., RequiredFieldViolation, StringLengthViolation) that your controller or view can use to generate localized UI feedback
  • Support for running System.ComponentModel.DataAnnotations attributes against a model object, to produce a collection of RuleViolation objects if there were problems
  • A custom model binder that knows how to generate and throw a suitable BusinessRuleException if there were setter exceptions
  • A helper for generating client-side validation code based on System.ComponentModel.DataAnnotations attributes (perhaps supporting multiple JS validation libraries)

Prevent Cross-Site Request Forgery (CSRF) using ASP.NET MVC’s AntiForgeryToken() helper

Update: Since the Release Candidate of ASP.NET MVC, these anti-forgery helpers have been promoted to be included in the core ASP.NET MVC package (and not in the Futures assembly).

Cross-site scripting (XSS) is widely regarded as the number one security issue on the web. But since XSS gets all the limelight, few developers pay much attention to another form of attack that’s equally destructive and potentially far easier to exploit. Your application can be vulnerable to cross-site request forgery (CSRF) attacks not because you the developer did something wrong (as in, failing to encode outputs leads to XSS), but simply because of how the whole Web is designed to work. Scary!

How CSRF works

So, what’s it all about? All web application platforms are potentially vulnerable to CSRF, but in this post I’ll focus on ASP.NET MVC. Imagine you have a controller class as follows:

public class UserProfileController : Controller
{
    public ViewResult Edit() { return View(); }
 
    public ViewResult SubmitUpdate()
    {
        // Get the user's existing profile data (implementation omitted)
        ProfileData profile = GetLoggedInUserProfile();
 
        // Update the user object
        profile.EmailAddress = Request.Form["email"];
        profile.FavoriteHobby = Request.Form["hobby"];
        SaveUserProfile(profile);
 
        ViewData["message"] = "Your profile was updated.";
        return View();
    }
}

This is all very normal. First, the visitor goes to Edit(), which renders some form to let them change their user profile details. Secondly, they post that form to SubmitUpdate(), which saves the changes to their profile record in the database. There’s no XSS vulnerability here. Everything’s fine, right? We implement this sort of thing all the time…

Unfortunately, this innocent controller is an easy target for CSRF. Imagine that an attacker sets up the following HTML page and hosts it on some server of their own:

<body onload="document.getElementById('fm1').submit()">
    <form id="fm1" action="http://yoursite/UserProfile/SubmitUpdate" method="post">
        <input name="email" value="hacker@somewhere.evil" />
        <input name="hobby" value="Defacing websites" />
    </form>
</body>

Next, they somehow persuade a victim to visit this page (basic social engineering, look it up). When this HTML page loads, it submits a valid form post to /UserProfile/SubmitUpdate on your server.

Assuming you’re using Windows authentication or some kind of cookie-based authentication system such as Forms Authentication, the automated form post will be processed within the victim’s established authentication context, and will successfully update the victim’s email address to something under the attacker’s control. All the attacker has to do now is use your “forgotten password” facility, and they’re taken control of the victim’s account.

Of course, instead of changing an victim’s email address, they can perform any action that the victim can perform with a single POST request. For example, they might be able to grant administrative permissions to another account, or post something defamatory to a CMS.

Ways to stop CSRF

There are two main ways to block CSRF:

  • Check that incoming requests have a Referer header referencing your domain. This will stop requests unwittingly submitted from a third-party domain. However, some people disable their browser’s Referer header for privacy reasons, and attackers can sometimes spoof that header if the victim has certain versions of Adobe Flash installed. This is a weak solution.
  • Put a user-specific token as a hidden field in legitimate forms, and check that the right value was submitted. If, for example, this token is the user’s password, then a third-party can’t forge a valid form post, because they don’t know each user’s password. However, don’t expose the user’s password this way: Instead, it’s better to use some random value (such as a GUID) which you’ve stored in the visitor’s Session collection or into a Cookie.

Using the AntiForgeryToken helpers

With Preview 5, Microsoft has added a set of helpers to the “futures” assembly, Microsoft.Web.Mvc.dll,The core ASP.NET MVC package includes a set of helpers that give you a means to detect and block CSRF using the “user-specific tokens” technique.

To use these helpers to protect a particular form, put an Html.AntiForgeryToken() into the form, e.g.,

<% using(Html.Form("UserProfile", "SubmitUpdate")) { %>
    <%= Html.AntiForgeryToken() %>
    <!-- rest of form goes here -->
<% } %>

This will output something like the following:

<form action="/UserProfile/SubmitUpdate" method="post">
    <input name="__RequestVerificationToken" type="hidden" value="saTFWpkKN0BYazFtN6c4YbZAmsEwG0srqlUqqloi/fVgeV2ciIFVmelvzwRZpArs" />
    <!-- rest of form goes here -->
</form>

At the same time, Html.AntiForgeryToken() will give the visitor a cookie called __RequestVerificationToken, with the same value as the random hidden value shown above.

Next, to validate an incoming form post, add the [ValidateAntiForgeryToken] filter to your target action method. For example,

[ValidateAntiForgeryToken]
public ViewResult SubmitUpdate()
{
    // ... etc
}

This is an authorization filter that checks that:

  • The incoming request has a cookie called __RequestVerificationToken
  • The incoming request has a Request.Form entry called __RequestVerificationToken
  • These cookie and Request.Form values match

Assuming all is well, the request goes through as normal. But if not, boom!, there’s an authorization failure with message “A required anti-forgery token was not supplied or was invalid”.

This prevents CSRF because even if a potential victim has an __RequestVerificationToken cookie, an attacker can’t find out its value, so they can’t forge a valid form post with the same value in Request.Form. But legitimate users aren’t inconvenienced at all; the mechanism is totally silent.

Using salt

Salt? What? In case you want to protect multiple forms in your application independently of each other, you can use a “salt” value when you call Html.AntiForgeryToken(), e.g.,

<%= Html.AntiForgeryToken("someArbitraryString") %>

… and also in [ValidateAntiForgeryToken], e.g.,

[ValidateAntiForgeryToken(Salt="someArbitraryString")]
public ViewResult SubmitUpdate()
{
    // ... etc
}

Salt is just an arbitrary string. A different salt value means a different anti-forgery token will be generated. This means that even if an attacker manages to get hold of a valid token somehow, they can’t reuse it in other parts of the application where a different salt value is required. (If anyone can suggest other use cases for salt, please let me know.)

Limitations of the Anti-Forgery helpers

ASP.NET MVC’s anti-CSRF helpers work very nicely, but you should be aware of a few limitations:

  • All legitimate visitors must accept cookies (otherwise, [ValidateAntiForgeryToken] will deny their form posts). Arguably this isn’t a limitation, because unless visitors allow cookies, you probably don’t have anything to protect anyway.
  • It only works with POST requests, not GET requests. Arguably this isn’t a limitation, because under the normal HTTP conventions, you shouldn’t be using GET requests for anything other than read-only operations.
  • It’s easily bypassed if you have any XSS holes on your domain. An XSS hole would allow an attacker to read a victim’s anti-forgery token value, then use it to forge valid posts. So, don’t have XSS holes!
  • It relies on the potential victim’s browser implementing cross-domain boundaries solidly. Browsers are supposed to stop foreign domains from reading your app’s response text and cookies, and are supposed to stop foreign domains from writing cookies to your domain. If an attacker manages to find a way around this, they can bypass [ValidateAntiForgeryToken]. Of course that’s not supposed to be possible. For the most part, modern browsers block this line of attack.

In conclusion, ASP.NET MVC’s anti-CSRF helpers are easy to use, and work very nicely thank you!