Aaronontheweb

Hacking .NET and Startups

How to Securely Verify and Validate Image Uploads in ASP.NET and ASP.NET MVC

One of the more interesting things I had to do as part of building XAPFest was handle bulk image uploads for screenshots for applications and user / app icons. Most of the challenges here are UI-centric ones (which I resolved using jQuery File-Upload) but the one security challenge that remains outstanding is ensuring that the content uploaded to your servers is safe for your users to consume.

Fortunately this problem isn't too hard to solve and doesn't require much code in C#.

Flawed Approaches to Verifying Image Uploads

Here's what I usually see when developers try to allow only web-friendly image uploads:

  1. File extension validation (i.e. only allow images with .png, .jp[e]g, and .gif to be uploaded) and
  2. MIME type validation.

So what's wrong with these techniques? The issue is that both the file extension and MIME type can be spoofed, so there's no guarantee that a determined hacker might not take a js. file, slap an extra .png extension somewhere in the mix and spoof the MIME type.

Stronger Approach to Verifying Image Uploads: GDI+ Format Checking

Every file format has to follow a particular codec / byte order convention in order to be read and executed by software. This is as true for proprietary formats like .pptx as it is for .png and .gif.

You can use these codecs to your advantage and quickly tell if a file is really what it says it is - you quickly check the contents of the file against the supported formats' codecs to see if the content fits into any of those specifications.

Luckily GDI+ (System.Drawing.Imaging), the graphics engine which powers Windows, has some super-simple functions we can use to perform this validation. Here's a bit of source you can use to validate a file against PNG, JPEG, and GIF formats:

using System.Drawing.Imaging;
using System.IO;
using System.Drawing;

namespace XAPFest.Providers.Security
{
    /// 
    /// Utility class used to validate the contents of uploaded files
    /// 
    public static class FileUploadValidator
    {
        public static bool FileIsWebFriendlyImage(Stream stream)
        {
            try
            {
                //Read an image from the stream...
                var i = Image.FromStream(stream);

                //Move the pointer back to the beginning of the stream
                stream.Seek(0, SeekOrigin.Begin);

                if (ImageFormat.Jpeg.Equals(i.RawFormat))
                    return true;
                return ImageFormat.Png.Equals(i.RawFormat) 
|| ImageFormat.Gif.Equals(i.RawFormat);
            }
            catch
            {
                return false;
            }
        }

    }
}

All this code does is read the Stream object returned for each posted file into an Image object, and then verifies that the Image supports one of three supported codecs1.

This source code has not been tested by security experts, so use it at your own risk.

If you have any questions about how this code works or want to learn more, please drop me a line in the comments below or on Twitter.

Bonus: How Do I Make Sure Files Are below [X] Filesize?

Since I had this source code lying around anyway, I thought I would share it: 

public static bool FileIsWebFriendlyImage(Stream stream, long size)
        {
            return stream.Length <= size && FileIsWebFriendlyImage(stream);
        }
    }

Super-simple, like I said, but it gets the job done. Express the maximum allowable size as a long and compare it against the length of the stream

 


1The other important catch to note here is that I move the Stream's pointer back to the front of the stream, so it can be read again by the caller which passed the reference to this function.

If you enjoyed this post, make sure you subscribe to my RSS feed!



How-To: Remote Validation in ASP.NET MVC3

ASP.NET MVC3 has been a major boon to my productivity as a web developer since I started using it at the beginning of November – the new Razor view engine has been attracting most of the attention with this iteration of MVC, but one extremely sexy feature has gone unnoticed thus far: Remote validation.

Remote validation was one of the new features added in the November Release Candidate (RC) for MVC3 – I had a chance to demo it in front of the LA .NET User Group last night and it was a monster hit.

Example:

You’ve all seen remote validation before – ever see one of these when you’re signing up for a service like Twitter?

image

That’s remote validation at work. Twitter, Facebook et al make an AJAX call to a remote service hook that checks the database to see if the username is available as the user types it. It’s a major user experience improvement as they get that feedback instantly instead of having to wait until after they fill out the rest of the form and submit it.

In the past with ASP.NET MVC you had to write your own custom jQuery scripts on top of the jQuery validation engine to achieve this end-result; in ASP.NET MVC3 this is taken care of for you automatically!

Let me show you how it works:

1. Decorate a model class with the Remote attribute

Take the class you want to validate and decorate the attributes you need to remotely validate with the Remote attribute.

public class NewUser
{
	[Remote("UserNameExists", "Account", "Username is already taken.")]
	public string UserName { get; set; }

	[Remote("EmailExists", "Account", "An account with this email address already exists.")]
	public string EmailAddress { get; set; }

	public string Password { get; set; }
}

In both of these instances of the Remote attribute, I’ve passed the following arguments:

  • The name of an action method;
  • The name of the controller where the action method lives; and
  • A default error message should user input fail this validation challenge.

2. Implement an action method to support your Remote attribute

You will need to implement an action method that supports your Remote attribute. Add an action method which returns a JsonResult to the controller you named in your Remote attribute arguments. In this example I’ll need to add these action methods to the “Account” controller:

public JsonResult UserNameExists(string username)
{
    var user = _repository.GetUserByName(username.Trim());
    return user == null ? 
		Json(true, JsonRequestBehavior.AllowGet) : 
		Json(string.Format("{0} is not available.", username),
			JsonRequestBehavior.AllowGet);
}

public JsonResult EmailExists(string emailaddress)
{
    var user = _repository.GetUserByEmail(emailaddress.Trim());
    return user == null ?
		Json(true, JsonRequestBehavior.AllowGet) : 
		Json(
			string.Format("an account for address {0} already exists.",
			emailaddress), JsonRequestBehavior.AllowGet);
}

 

If the repository returns null, meaning that a user account with this particular user name or email address doesn’t already exist, then we simply return a JsonResult with a value of true and go off on our merry way. This will suppress the jQuery validation library from raising a validation error.

If the action method returns false, then jQuery will raise a validation error and display the default error message provided in the Remote attribute arguments – if you didn’t provide a default error message yourself then the system will use an ambiguous default one.

If the action method returns a string, jQuery will raise a validation error and display the contents of the string, which is what I’m doing here in this example.

Small Gotcha – Naming Action Method Parameters

There’s one small gotcha that can be easy to miss – the name of the argument on your action method must match the name of your property on your model. If I changed the EmailExists body to look like this:

public JsonResult EmailExists(string email);

Then ASP.NET would pass a null value to this method.

Case 1: parameter name matches the name of the model’s property

image

Case 2: parameter name does not match the name of the model’s property

image

This is because the jQuery parameter takes the name of the property on the model and passes it as a querystring argument to your action method – here’s what your validation request looks like in Firebug:

[host]/account/emailexists?area=an%20account%20with%20this%20email%20address%20already%20exists.&EmailAddress=test%40test.com

The ASP.NET MVC model-binder isn’t all-knowing – it’s not going to be able to tell that EmailAddress and email are the same thing, thus it ultimately won’t bind an argument to your action method, hence why the null value is passed.

If you follow the convention of using a common name for your Remote validator action method arguments and your model properties, you won’t run into this issue.

UPDATE: Thanks to Rick Anderson for directing me to the much more extensive MSDN documentation on how to implement custom Remote validaiton in ASP.NET MVC3.

If you enjoyed this post, make sure you subscribe to my RSS feed!



How to Use the Microsoft Enterprise Library Validation Application Block, Part 1

All user input is evil, and you know it. Since the inception of .NET, ASP.NET developers have had access to the ASP.NET Validators Control Library, which made the previously tedious process of validating form input simple and in many instances, trivial. Microsoft's Enterprise Library makes this familiar ASP.NET functionality available at the object level in your applications via the Validation Application Block, which I've been using throughout some of my new projects.

It can take a while to get familiar with the Validation Application Block, so my goal here is to show you how to quickly get started with it using attribute-based validation, which in my opinion is the simplest way to implement validation. By the time you're doing reading this you'll know how to use built-in validators such as the RangeValidator, RegexValidator, DateTimeRangeValidator, and so forth.

Introduction to the Validation Application Block Built-in Validators

What can the Validation Application Block do, exactly? Well, here's a list of the validators that are built-in to the Validation Application Block:

  • The NotNullValidator which requires an instantiated data member.
  • The RangeValidator and the DateTimeRangeValidator, which check to see that a data member's value falls within a specified set of boundary conditions.
  • A slew of validators for testing strings:
    • The StringLengthValidator for testing that a string's length falls within a specified set of boundary conditions.
    • The RegexValidator and the ContainsCharactersValidator, both of which test that a string contains a specified target sub-string or expression.
    • The TypeConversionValidator, which tests to see if a string can be converted to some other type.
  • Validators for testing the comparative values of fields, such as the PropertyComparisonValidator and the RelativeDateTimeValidator.
  • The DomainValidator and the EnumValidator, which checks to see that a value falls within a specified set of acceptable values.
  • The ObjectValidator and the ObjectCollectionValidator, which are used to invoke validation methods on data members which are also validation-enabled objects.

In addition to these built-in validators the Validation Application Block also includes methods for creating user-defined validators and for composing validators into ValidatorCompositions which can process more complex validation rules.

How to Add the Validation Application Block to a Visual Studio Project

Before you worry about how to start adding validators to all of your business objects, you need to include the necessary assemblies into your Visual Studio / Visual [X] Express projects. After you've downloaded and installed the Enterprise Library 5.0 or *Enterprise Library 4.1 binaries onto your system, open your existing Visual Studio Project or create a new one. Open the Solution Explorer, expand the References folder, right click on the References folder and click on Add Reference

Select the following two assemblies (typically they'll be installed to your system's GAC so you won't need to go hunting for them) - Enterprise Library Shared Library and Enterprise Library Validation Application Block. The Validation Application Block depends on Shared Library (as do many other components in the Enterprise Library), and you'll run into some run-time errors without it included in your project's References folder.

If you're designing a WinForms or an ASP.NET application, you may want to include the special Validation Application Block WinForms Integration or Validation Application Block ASP.NET Integration assemblies, but if you're designing validation rules for individual business object classes and not presentation-layer events, you may not need them.

Example: Using Built-in Validators for Validating an Address

I'm going to end this post with a simple example which will show you how to get started using the Validation Application Block's attribute-based validators, and I'll expand on this in subsequent posts. We're going to work with a simple class which contains an address based in the United States:

public class USAddress
{
    public string RecipientName { get; set; }
    public string Street { get; set; }
    public string City { get; set; }
    public string State { get; set; }
    public string ZipCode { get; set; }
}

We know that addresses based in the United States need to comply with the following business rules:

  1. All of these fields must be filled in;
  2. A street must contain a street name and a street number;
  3. A state code must be two characters in length; and
  4. A zip code contains exactly five or nine digits.

 So, let's examine how we can use some validators to ensure that this business object enforces these business rules. The first step is to ensure that our source code references the correct namespace:

using Microsoft.Practices.EnterpriseLibrary.Validation.Validators;

 Next, let's go ahead and enforce business rule #1 - all address fields must be filled in. We can accomplish by decorating each field with NotNullValidator attribute:

using System;
using System.Text;
using Microsoft.Practices.EnterpriseLibrary.Validation.Validators;

namespace Business_Entities
{
    public class USAddress
    {
        [NotNullValidator()]
        public string RecipientName { get; set; }

        [NotNullValidator()]
        public string Street { get; set; }

        [NotNullValidator()]
        public string City { get; set; }

        [NotNullValidator()]
        public string State { get; set; }

        [NotNullValidator()]
        public string ZipCode { get; set; }
    }
}

Let's see if this works. Here's some simple code we can use to test the validators by calling the Validator.Validate() method against an instance of this class.

using System;
using System.Text;
using Business_Entities;
using Microsoft.Practices.EnterpriseLibrary.Validation;

namespace Basic_Validation__Console_
{
    class Program
    {
        static void Main(string[] args)
        {
            //Create a blank test address
            USAddress testaddress = new USAddress();

            //Create a new validator using the ValidationFactory method
            Validator validator = ValidationFactory.CreateValidator<USAddress>();
            ValidationResults results = new ValidationResults();
            validator.Validate(testaddress, results);

            Console.Write("Is the test address valid? {0}", results.IsValid);
            //Stop the console screen from disappearing immediately.
            Console.ReadKey(); 
        }
    }
}

And what happens when we run this test code? 

Smells like success to me. Let's re-run this test using a valid address.

//A real(ish) test address
USAddress testaddress = new USAddress();
testaddress.RecipientName = "Bob Jones";
testaddress.City = "Beverly Hills";
testaddress.State = "CA";
testaddress.Street = "1205 Rodeo Drive";
testaddress.ZipCode = "90210";

And the result is: 

Looks like the NotNullValidator is working as intended. But wait a minute, how did this ValidationFactory.CreateValidator() method know how to create a Validator object that properly validates any instance of a USAddress object?

Because we decorated the USAddress class with one or more Validator attributes, the underlying Validation Application Block library automatically implements and registers a Validator class designed to support the USAddress class. Internally, the Validation Application Block uses an instance of Unity to resolve a custom validator for the USAddress class on-the-fly.

Let's enforce some of the other business rules, starting with the state code.

[NotNullValidator()]
[StringLengthValidator(2, RangeBoundaryType.Inclusive, 2, RangeBoundaryType.Inclusive)]
public string State { get; set; }

As you can see I've added a StringLengthValidator in addition to the NotNullValidator that was already in place (yes, you can add more than one Validator attribute to a field.) I've specified the following parameters for this validator:

  1. lowerBound = 2
  2. lowerBound RangeBoundaryType = Inclusive (meaning that this value is included in the range of acceptable string length values)
  3. upperBound = 2
  4. upperBound RangeBoundaryType = Inclusive (this value is also included in the range of acceptable string length values)

There are three possible range boundary types:

  1. Inclusive, which is the equivalent of [] in traditional mathematical syntax;
  2. Exclusive, which is the equivalent of () in traditional mathematical syntax; and
  3. Ignore, which is the equivalent of (infinity, or infinity) in traditional mathematical syntax.

Now the next change we need to make is to check for a valid zip code, which can be either 9 or 5 digits. I'm going to do this using a RegexValidator - another way to do this is to create an ValidatorComposition (an "Or" type as opposed to an "And" type) with two different string length validators. Here's what our RegexValidator looks like:

[NotNullValidator()]
[RegexValidator(@"^(\d{5}-\d{4}|\d{5}|\d{9})$|^([a-zA-Z]\d[a-zA-Z] \d[a-zA-Z]\d)$")]
public string ZipCode { get; set; }

Hat tip to Microsoft's Patterns & Practices group for providing me with the regular expression for validating US zip codes.

Now, our source code for testing this validation has not changed one bit - all of the changes to the validator for the USAddress class occur behind the scenes. Once I recompile my class library and re-run the test I should get a "validation successful" message back from the console: 

Well hell yeah, that'll do it! If I wanted to validate the last business rule (the street address must contain both a street name and number,) I could write another RegexValidator for that and hook it up in much the same way as my RegexValidator for zip codes. And that's all it really takes to get started with the Microsoft Enterprise Library Validation Application Block. In my next article on the subject I'll include some examples on how / when you should use ObjectValidators and why they're so useful.

*Although the latest version of the Validation Application Block is version 5.0, my example code uses Enterprise Library version 4.1, namely because my individual development environment has some issues with the Validation Application Block 5.0 and the GAC. Given that there are no major syntactical differences between the two versions, I'm going to do what any economical programmer would do and use what works instead of dicking around with configuration files, assembly directories, and things that are frankly tangential to developing applications.

If you enjoyed this post, make sure you subscribe to my RSS feed!



Search

About

My name is Aaron, I'm an entrepreneur and a .NET developer who develops web, cloud, and mobile applications.

I left Microsoft recently to start my own company, MarkedUp - we provide analytics for desktop developers, focusing initially on Windows 8 developers.

You can find me on Twitter or on Github!

Recent Comments

Comment RSS

Sign in