Securing MVC forms from parameter manipulation
A common attack on web applications is parameter manipulation. This attack involves messing with some data - whether it's query string data, cookie data, form data, etc which then allows them to circumvent an assumed protection.
Let's take the scenario of someone editing their own User settings - password, address, etc.
Generally - some record/object identifier is emitted to the page. This could be done in web forms via something like
txtUserId.Text = user.UserId;
txtUserId.Visible=false;
When this is rendered, we will have roughly the following:
<input type="hidden" id="txtUserId" value="xxx" />
where "xxx" happens to be the primary key of the record that will be updated when the user posts back.
In MVC - this generally happens a lot more 'hidden' so to say. We generally would have code like such:
User user = UserRepository.GetUser(userId);
return View(user);
//where the user class is
public class User
{
[HiddenInput(DisplayValue = false)]
public int UserId { get; set; }
[Required]
public string UserName { get; set; }
[Required]
public string Password { get; set; }
}
In that case a field named UserId is rendered, basically the same as shown above as <input .... />
So - what happens if right before postback a user modifies this hidden form field and changes the UserId? They are then able to update a record that _isn't their_!!!!!
The smartest thing in this case to do is of course to make sure the user id 'belongs' to this user on the server side. If the user is trying to update a record they don't have access to, we throw an exception.
However these checks invariably get missed as a developer forgets to add this code in, or there was a refactoring that left it out, etc.
One solution web forms has - is if this data is tampered with on the client side for the hidden field, an exception will be throw because of EnableEventValidation=True
This is why you want to keep on EnableEventValidation=true and also EnableViewStateMac=true otherwise ViewState could be tampered with.
This creates a problem though for javascript that you intentionally want to change for ex. the selected value in a combo box (and add a new value) Web Forms will blow up on post back, as such developers often turn these protections off without understanding the risks. In the described case here you have to tell web forms what values you will be adding to the combo box although unfortunately we don't always know this.
In MVC , we have no such built in protections unfortunately. While it is true developers should ideally verify the data on the server side, I would venture to say it happens not as much as it should. Web Forms has built in validation - so why can't MVC! It can, and I created a fine grained solution so you can specify exactly what values you want to check.
On our view, we call Html.AntiModelInjectionFor which is from the download at the end of this post.
@using Security_MVC.Extensions
..
..
@using (Html.BeginForm())
{
@Html.AntiModelInjectionFor(o => o.UserId);
@this.Html.AntiForgeryToken();
@Html.EditorForModel();
<input type="submit" value="Submit" />
}
And on our controller
@using Security_MVC.Extensions
..
..
@using (Html.BeginForm())
{
@Html.AntiModelInjectionFor(o => o.UserId);
@this.Html.AntiForgeryToken();
@Html.EditorForModel();
<input type="submit" value="Submit" />
}