Browse Tag: asp

How to create your own ASP .NET MVC model binder

Model binding is the process of converting POST data or data present in the Url into a .NET object(s).  ASP .NET MVC makes this very simple by providing the DefaultModelBinder.  You’ve probably seen this in action many times (even if you didn’t realise it!), but did you know you can easily write your own?

A typical ASP .NET MVC Controller

You’ve probably written or seen code like this many hundreds of times;

public ActionResult Index(int id)
    using (ExceptionManagerEntities context = new ExceptionManagerEntities())
        Error entity = context.Errors.FirstOrDefault(c => c.ID == id);

<pre><code>    if (entity != null)
        return View(entity);                    

return View();


Where did Id come from? It probably came from one of three sources; the Url (Controller/View/{id}), the query string (Controller/View?id={id}), or the post data.  Under the hood, ASP .NET examines your controller method, and searches each of these places looking for data that matches the data type and the name of the parameter.  It may also look at your route configuration to aid this process.

A typical controller method

The code shown in the first snippet is very common in many ASP .NET MVC controllers.  Your action method accepts an Id parameter, your method then fetches an entity based on that Id, and then does something useful with it (and typically saves it back to the database or returns it back to the view).

You can create your own MVC model binder to cut out this step, and simply have the entity itself passed to your action method. 

Take the following code;

public ActionResult Index(Error error)
    if (error != null)
        return View(error);

<pre><code>return View();


How much sweeter is that?

Create your own ASP .NET MVC model binder

You can create your own model binder in two simple steps;

  1. Create a class that inherits from DefaultModelBinder, and override the BindModel method (and build up your entity in there)
  2. Add a line of code to your Global.asax.cs file to tell MVC to use that model binder.

Before we forget, tell MVC about your model binder as follows (in the Application_Start method in your Global.asax.cs file);

ModelBinders.Binders.Add(typeof(Error), new ErrorModelBinder());

This tells MVC that if it stumbles across a parameter on an action method of type Error, it should attempt to bind it using the ErrorModelBinder class you just created.

Your BindModel implementation will look like this;

public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    if (bindingContext.ModelType == typeof(Error))
        ValueProviderResult valueProviderValue = bindingContext.ValueProvider.GetValue("id");

<pre><code>    int id;
    if (valueProviderValue != null &amp;&amp; int.TryParse((string)valueProviderValue.RawValue, out id))
        using (ExceptionManagerEntities context = new ExceptionManagerEntities())
            return context.Errors.FirstOrDefault(c =&gt; c.ID == id);

return base.BindModel(controllerContext, bindingContext);


The code digested;

  1. Make sure that we are only trying to build an object of type Error (this should always be true, but just as a safety net lets include this check anyway).
  2. Get the ValueProviderResult of the value provider we care about (in this case, the Id property).
  3. Check that it exists, and that its definitely an integer.
  4. Now fetch our entity and return it back.
  5. Finally, if any of our safety nets fail, just return back to the model binder and let that try and figure it out for us.

And the end result?


Your new model binder can now be used on any action method throughout your ASP .NET MVC application.


You can significantly reduce code duplication and simplify your controller classes by creating your own model binder.  Simply create a new class that derives from DefaultModelBinder and add your logic to fetch your entity.  Be sure to add a line to your Global.asax.cs file so that MVC knows what to do with it, or you may get some confusing error messages.

5 easy security enhancements for your ASP .NET application

Protecting web applications against unauthorised access is somewhat of a dark art, but there are simple steps you can take to ensure that you are protected against the most common security risks.

Cross Site Request Forgery (CSRF / XSRF)

Problem: Cross site request forgery (CSRF / XSRF) is the process of tricking a legitimate user of your website into posting data to the web server without their knowledge.  This type of attack is typically executed using a malicious link in an email, or by social engineering.

Typically, an attacker will craft a web page that has a form with fields named that match the properties that an ASP .NET MVC controller action method is expecting.  The form is then submitted (perhaps tucked away in an invisible iframe) and as long as the user has an active session open, the form will be processed as normal.  Such an attack could be as simple as posting a comment to a website without the users knowledge, or, say, in the case of a banking application, transfer money to the attacker.

Solution: To resolve the problem in ASP .NET MVC, you need to make use of  Anti-Forgery Tokens.  An anti-forgery token is a hidden field with a unique id that is placed on the form.  When the form is posted, the anti-forgery token is also passed along with the request and validated.  If the token is invalid, or missing, an exception is thrown.  Due to the random nature of the token, its impossible for an attacker to guess, meaning the only way to post to the server is via a page that originated from it.


In all your views that have forms that will be posted back to the server, simply use the AntiForgeryToken method on the HtmlHelper;

@using (Html.BeginForm("Login", "Account", new { ReturnUrl = ViewBag.ReturnUrl }, FormMethod.Post))

Then add the ValidateAntiForgeryToken attribute to your action method;

public async Tas<ActionResult> Login(LoginViewModel model, string returnUrl)

Open Redirection

Problem: Open redirection is a form of phishing that, when a specific website is targeted, can be used to steal information, such as email address and password from a legitimate user.  The problem occurs when redirecting a user to a page based on information stored in the query string.  A malicious user could craft a URL to include a redirect to a website that looks identical to the original, which requests log-in credentials, then redirects the user back to the original website.

A user receives an email asking them to check out a page on a website, which they click.  The page requires the user to log in.  Rather than linking directly to the actual page, the link would point to the log in page, with a return Url;

Notice a very subtle difference between the source domain ( and the destination domain (  Easy to miss.  The user is prompted to enter their credentials, and they are then redirected to the returnUrl.  The user is actually shown an identical log in page which says that they have entered their credentials incorrectly, please try again.  The user re-enters the credentials and they are redirected back to the original site (which works because their credentials were correct in the first place).

This was a bigger problem in earlier versions of ASP .NET MVC because the vulnerability was present out-of-the-box.  The problem still exists when using the Redirect method in conjunction with the query string;


There are two solutions;

  • Don’t use the Redirect method at all.  Instead use RedirectToLocal or even better, use RedirectToRoute or RedirectToAction.
  • Only ever use hard coded return Urls when calling the Redirect method.

Cross Site Scripting (XSS)

Problem: Cross site scripting (XSS) is when an attacker uses a form on your website to inject script onto a page.  The script can be literally anything, from annoying popups/banner advertisements, to more sinister code designed to steal confidential information from a legitimate user.

By default in the current version of Razor (the ASP .NET MVC default view engine) all data originating from properties on your model is encoded.  Also, if a user tries to submit HTML / JavaScript using any forms on your website, an exception will be thrown and the data will be rejected.  The developer has to explicitly allow Html (using the aptly named AllowHtml attribute) or turn off validation (using the ValidateInput(false)) attribute.

If you absolutely have to accept Html on your website, make sure you implement a white list of allowed tags, rather than a blacklist of disallowed tags.

There is a lot of information available regarding this type of attack (as it is very common).  A good video I would suggest watching is The HaaHa Show: Microsoft ASP .NET MVC Security with Haack and Hanselman.

Over-posting (A.K.A. Mass Assignment)

Problem: It is possible to post additional data to the server along with a request, even for fields for which the developer never intended to be accessible by the client.  The problem is due to the fact that the model binder in ASP .NET MVC matches up all the post data to all problems on your entities/models without discrimination.

You have a Customer object, which contains details about a customer (Name, Address etc.).  You create a view for the user to edit their details, passing the Customer object to the view so that fields can be pre-populated.  When the user updates their details, and submits the form, the entity/model is saved back to the database.  However, your Customer object also has a navigation property called Orders, which contains all the orders that the customer has placed.  An attacker could submit a new order, which would be bound to the Orders property by the model binder, which may result in the customer receiving products that they haven’t actually paid for.


  1. Use the Bind attribute to either blacklist or whitelist properties on your entity/model.  The model binder will see the attribute, and either ignore or only bind the properties you have stated.
public void Save([Bind(Exclude = "Orders")]Entity entity)
{     //Logic
  1. Create a view model for each specific view, and only include the properties that you want exposed to the client (i.e. omit the Orders navigation property).

SSL for the login / registration process

Problem: SSL is required to ensure that confidential information, such as user credentials or credit card information is transferred from the clients web browser to your server in a secure manner.  Failure to do so will result in this information being susceptible to interception by a third party.

It is easy to see this information being posted to the server using a debugging proxy (such as Fiddler).

Example message sent from the browser (Google Chrome in this case) to a web server when attempting to log in to a website;

POST http://localhost:64429/Account/Login HTTP/1.1
Host: localhost:64429
Connection: keep-alive
Content-Length: 183
Cache-Control: max-age=0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Origin: http://localhost:64429
User-Agent: Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/32.0.1700.76 Safari/537.36
Content-Type: application/x-www-form-urlencoded
Referer: http://localhost:64429/Account/Login
Accept-Encoding: gzip,deflate,sdch
Accept-Language: en-GB,en-US;q=0.8,en;q=0.6
Cookie: __RequestVerificationToken=dAaXZYTCsNTKUZT7cxKqTEVOXjii9Md-VzxfY9-XxcSW1C_3mEV7OK2Wrp_bbOsEB555GNWv7RK6p9soYpKljwtTsXL7zldikJB4aK-NYog1


Look closely, and you will see that the Request Verification Token includes the raw username/password;


Solution: You should purchase an SSL certificate and apply it to your website.  Doing so will encrypt the traffic so that it cannot be easily viewed between the source and destination.

If your website is hosted using Internet Information Services (IIS);

  • Open the IIS Manager (inetmgr.exe)
  • Select the root level node under “Connections”
  • Double click “Server Certificates”
  • Import the certificate using the links on the “Actions” pane
  • Click on your website
  • Add a new https binding and select the SSL certificate you just imported.