MVC3 Remote Validation and AntiForgeryToken - asp.net-mvc

After reading about XSS attacks I have applied the AntiForgeryToken to my website using the ValidateAntiForgeryTokenWrapperAttribute described in this article:
http://weblogs.asp.net/dixin/archive/2010/05/22/anti-forgery-request-recipes-for-asp-net-mvc-and-ajax.aspx
It seems to work well however I've come across a problem when using Remote Validation in MVC3. I have a ValidationController which contains all of the common validation within my site and if I apply the ValidateAntiForgeryTokenWrapperAttribute to it then the remote validation no longer works and I get an 'A required anti-forgery token was not supplied or was invalid.' exception logged in Elmah. I've tried debugging this and it doesn't even hit the controller action before throwing the exception. I assume this happens because the remote validation doesn't know to pass the AntiForgeryToken to the controller - has anyone else had this problem or knows whether the two are not meant to be used together?
It also made me question whether I should be using the ValidateAntiForgeryTokenWrapperAttribute on every controller or not, any thoughts?

In the remote attribute do as below:
[Remote("MyValidationMethod","MyController", HttpMethod = "POST", AdditionalFields = "__RequestVerificationToken")]
public object MyField { get; set; }
the AdditionalFields property can accept comma separated fields names in the form; the __RequestVerificationToken is the name of the hidden field which contains the AntiForgeryToken.

I haven't used Remote Validation. However I had similar experience with AntiForgeryToken. When I had it applied for all the actions in my controller. Later, I removed it from all the actions and applied to only those actions which were sending data back to database (insert/update/delete).
As it seems you have applied AntiForgeryToken validation attribute to entire controller, it will always create a new token value every time an action is executed and so when response goes back to client for remote validation action the value of token is different than what is on the form which gets submitted later for other actions.
You can remove AntiForgeryToken attribute from the controller and use it with other action apart from remote validation action or wherever you really need it.
//Instead of this
[ValidateAntiForgeryToken]
public class mycontroller
{
//...
}
//Do something like this
public class mycontroller
{
public ActionResult myotheraction ()
{ }
[ValidateAntiForgeryToken]
public ActionResult valdaitionaction ()
{ }
}

Related

ASP.NET MVC does not deserialize JSON correctly when Conten-Type is missing

Using ASP.NET MVC 4, I have a controller that accepts a JSON structure:
public class SomeDto {
...
public Boolean IsUnicornAlive { get; set; }
...
}
[HttpPost]
public ActionResult DoSomething(SomeDto dto) {
...
}
Now if this POST request comes in without Content-Type specified:
{
...
"IsUnicornAlive":true
...
}
the action still gets called but IsUnicornAlive property would be false. Essentially this is a problem on the side that created the request, but still I would expect ASP to not treat it silently and not call my action with defaulted values. The most appropriate handling in this case would be a 4xx error, 415 ideally. What would be the easiest way to implement it? Or is there a way to know that default values were used inside the controller action?
Note: this question is not about ASP.NET WebApi which does not have this issue. This is specifically about MVC.
How about having a custom ModelBinder for SomeDTO and parsing the incoming data yourself and deciding whether to deduce the values, add a modelstate error, etc...
Look at the ModelState property of the Controller. For example ModelState.Keys.Count == 0 when the default value was used but you might find something even nicer in that object.

Call ASP.NET's input validation after using ValidateInput(false)

When I bash my hands on special characters in the entry fields of my form, when I hit submit, all I get back is a blank page, as ASP.NET detected potentially malicious input and threw an error into my Event Log, all before reaching my MVC controller action.
I would much rather return an error to the user, so they don't just get a blank page. So I want to use ValidateInput(false) and then call the validator from inside my code, so I can send a response. How do I call the validator from an ASP.NET MVC action?
I am little confused with your question .Are you looking for invoking the code which does the model validation ? IF yes you can do that by checking the ModelState.IsValid property. If any of your validation fails, it will return false and in your view you will see the error messages. You can also specify some custom error message from your action method as well if you want to do so.
[HttpPost]
public ActionResult LogOn(LogOnModel model, string returnUrl)
{
if (ModelState.IsValid)
{
//Model validation is successful. Proceed to other stuff
//If you want to return some custom error message, do like this
ModelState.AddModelError("", "The user name is deactivated");
}
return View(model);
}
Do you want to validate input at server side?
You can use client side validation, you need to add regular expression attribute on your model property and set a nice error message for it. like below
[RegularExpression("your regex", ErrorMessage = #"please check your input .... ")]
public string MyInput { get; set; }

Map HttpPost data to model in MVC - How to capture data with "-" in name?

If I have a controller:
[HttpPost]
public ReturnType ControllerMethod(CustomModel c)
{
...
}
A third party is posting data to this method:
abc-xyz=testdata
One way, would be to use:
Request.Params["abc-xyz"]
However, in the case where the Request could contain malicious code, if any Request Parameter contained some problematic code, IIS would throw an HttpRequestValidationException as soon as Request.Params[""] is called.
Now, rather than turn off that validation everywhere, I'd like to map the posted data to my model. That way, if the "potentially malicious code" is contained in any parameter that isn't "abc-xyz", my application won't throw a HttpRequestValidationException. It will however, check the used paramaters, and throw a HttpRequestValidationException if the accessed data is potentially malicious.
How do I do that if the posted data has a dash/hyphen in the name?
I've tried a few variations including:
public class CustomModel
{
[Required]
public string abc_xyz
}
You should write your own ModelBinder in this case that transforms the values from the request into the ones needed for CustomModel.
To over come the request validation, you can turn it off with the ValidateInputAttribute on your controller action.

Returning data with viewmodel in POST request

I have a view model like such:
public class MyViewModel
{
public string Name { get; set; }
public List<Purchases> Purchases { get; set; }
}
This viewmodel is sent to a view that allows the user to edit the name property. The Purchases property is used only to create a dropdown box for it:
<%: Html.DropDownListFor(t => t.Name, new SelectList(Model.Purchases, "Value", "Text")) %></p>
This works fine.
However, when I perform server-side validation and then return to the View, I'm getting an object null reference error because the Purchases property is now set to null. I'm guessing this is because when the form is submitted because the Purchases property isn't bound to any editable control, it isn't being passed back with the viewmodel.
How can I prevent this happening? I want to send back the List to be send back with the Post request always.
You don't need to send back the list. If validation fails then simply rebuild the view model from scratch. One of the main selling points of MVC is how well it works in a stateless environment. Web Forms used ViewState to do this kind of thing, I don't think you want to replicate this kind of functionality though.
I like to have two overloaded Action methods for this (both with the same name but different method signatures). One with an [HttpGet()] attribute and the other with an [HttpPost()]. If your model is found to be invalid on the POST then simply return the "GET" method (NOTE: you'll need to to pass in any parameters required to rebuild the view).
When I say return, I mean:
return MyGetAction();
and not a Redirect to the GET action.
If the model is valid then you could/should perform a RedirectToAction() to a GET Action (this means if the user hits the refresh button it won't submit the form again, this is called the Post/Redirect/Get (PRG) pattern)
You'd have to create a hidden input for each of the elements in the list in addition to the select list. Having said, that I think caching the results of the query on the server is a better way to handle repopulating the list if you don't want to perform the query again. There's no sense in sending the data back across the wire if the server can just hang on to it. Personally, I wouldn't even bother with the caching unless it proved to be a performance bottleneck. Just populate the model selection list from the DB.
<% for (int i = 0; i < Model.Purchases.Length; ++i) { %>
<%: Html.Hidden( string.Format( "Purchases[{0}]", i ), Model.Purchases[i] ) %>
<% } %>
Lee Gunn is spot on. To make the answer a little more concrete, it is very common to re-build non-scalar values in the event ModelState is not valid. This can be done by simply returning the [HttpGet] version in your Controller. However, you could simply re-build the purchases collection manually. It's up to you.
[HttpGet]
public ActionResult MyView(string name)
{
//get entity and build up a view model
var entity = _myDb.GetEntity(name);
MyViewModel vm = AutoMapper.Map<Entity, MyViewModel>(entity);
return vm;
}
[HttpPost]
public ActionResult MyView(MyViewModel vm)
{
If(!ModelState.IsValid)
{
//here is one way of doing it
return MyView("");
//OR
vm.Purchases = GetSomePurchasesBro();
return View(vm);
}
//continue on persisting and doing the Post Redirect Get
}
P.S.
Calling
return MyView("");
can be replaced with
return MyView(vm.Name);
Both will do the same thing (provided you're using the Html Helper Extensions, i.e. Html.TextBoxFor(x=>x.Name))
Defaut model binding looks in the ModelState for attemptedValues when rendering Html. This is described here by Steve Sanderson.

Disable Model Validation in Asp.Net MVC

How do I disable Model validation for a single Action in a Controller ?
Or can I do it per model by registering the model type at startup somewhere ?
I want the ModelBinder to bind to the model, but afterwards it should not perform the model validation.
The reason why i dont want validation to happen is because i am trying to move the logic from the controllers to a service layer which will be responsible for validating the models as i cant assume that models being passed to a service contains valid data.
As I understand this is the recommend approach (to not have logic in the controllers), so I find it a bit strange that i cant seem to find anything about how the model validation can be disabled (per action or per model type).
Please notice that I dont want to disable model validation for the entire webapplication (by removing the validation providers), and i dont want to disable the input validation that checks for malicious code being posted.
UPDATE
I am using .Net 4.0 and MVC 3 Preview 1
Just remove the items you don´t need before checking if the model is valid
ModelState.Remove("Email");
if (ModelState.IsValid)
{
// your logic
}
I've solved this problem with this code:
public ActionResult Totals(MyModel model)
{
ModelState.Clear();
return View(model);
}
Not sure it's the right way though.
Unfortunately there seems to be no easy way to disable the model validation happening in the ModelBinder except for registering every single model type you don’t want to validate (including nested complex types) with a specific ModelBinder. It can be done with the following code:
ModelBinders.Binders[typeof(MyModelType)] = new NonValidatingModelBinder();
Creating a SkipValidationAttribute that can be attached to action methods or action method parameters doesn’t seem possible as it should be implemented in the ControllerActionInvoker, but there is no way of letting the ModelBinder know that it should do any validation in the SetProperty() and OnModelUpdated methods when calling BindModel() in the GetParameterValue() method.
I definitely dislike this addition in the 2.0 version, because, as you stated in your question, validation makes more sense in the Service layer. In this way you can reuse it in other non-web applications, and test it more easily without having to mock the auto-validation mechanism.
Validation in Controller layer is pointless because in this part you can only verify model data and not business rules. For example, think of a service responsible of adding new comments and a user that wants to post a new one, the data in the comment he/she is posting may be valid, but what happens if the user is banned to comment because of misbehavior in the past? You should do some validation in the Service layer to ensure this is not happening, and if it does, throwing an exception. In short, validation must be done in the Service layer.
I use xVal as my Validation framework because it's compatible with the DataAnnotationModel, allows me to place validation wherever I want and performs client-side validation without extra-effort, even remote-client side. This is how I use it at the beginning of each of my services, for example, a login service:
public void SignIn(Login login) {
var loginErrors = DataAnnotationsValidationRunner.GetErrors(login);
// Model validation: Empty fields?
if (loginErrors.Any())
throw new RulesException(loginErrors);
// Business validation: Does the user exist? Is the password correct?
var user = this._userRepository.GetUserByEmail(login.Email);
if (user == null || user.Password != login.Password)
throw new RulesException(null, "Username or password invalids");
// Other login stuff...
}
Simple, web-independent and easy... then, in the Controller:
public RedirectResult Login(Login login) {
// Login the user
try {
this._authenticationRepository.SignIn(login);
} catch (RulesException e) {
e.AddModelStateErrors(base.ModelState, "Login");
}
// Redirect
if (base.ModelState.IsValid)
return base.Redirect(base.Url.Action("Home"));
else return base.Redirect(base.Url.Action("Login"));
}
I would recommend you perform validation in both places rather than trying to turn off validation in the UI. I understand your point that the service cannot assume that it's being passed valid data - that is a valid concern and is the reason your service should have validation. But you should also have validation in your UI. This is also nice because you can have client-side validation to prevent user errors and give you users a better experience.
I know that this already been answered but what you really needed was to extend the DataAnnotationsValidatorProvider and override the GetValidators method.
Then, on startup, you would remove the DataAnnotationsValidatorProvider from ModelValidatorProviders.Providers and add your new one.
Needless to say, if you simply don't want any validation at all, you can simply have the GetValidators method returning an empty collection.
In my case, I need to remove validation only when submitting the forms while still keeping the client-side validation, hence the following:
public class DynamicModelValidatorProvider : DataAnnotationsModelValidatorProvider
{
GetValidators(ModelMetadata metadata, ControllerContext context, IEnumerable<Attribute> attributes)
{
if (context.HttpContext.Request.HttpMethod == "POST")
{
return new ModelValidator[] { };
}
return base.GetValidators(metadata, context, attributes);
}
}
I use
[ ValidateInput( false )]
Not sure if this prevents model validation, or only IIS submit validation.

Resources