Prologue: I've read many questions about fixing 'A potentially dangerous request' errors:
- A potentially dangerous Request.Form value was detected from the client
- ASP.NET MVC A potentially dangerous Request.Form value was detected from the client when using a custom modelbinder
- Handle "potentially dangerous Request.Form value..."
Among many others. I know how to fix it several different ways, however I do not want to turn off request validation.
I would like to add form validations to all string view model properties site-wide to guard against the "A potentially dangerous request" exception, since throwing an exception is not ideal if a user can correct their input. I've only been able to think of two different solutions:
Add a regular expression validator to each view model property that accepts a
string
datatype, which is executed on the browser. This will prevent form submissions, but is a lot of work coding wise for our applicationSomehow magically add a validator that is executed on each request that is triggered on all string view model properties that does this validation and gives the user a friendlier message than the Yellow Screen of Doom (tm).
The validation message that should show up by the form field, regardless of solution, should be something like:
The X field contains potentially dangerous characters. Either remove all '<' characters, or ensure a space or punctuation mark occurs before them.
Or something to that affect. Basically, I want a validation message that helps the user resolve the situation instead of blowing the whole application sky-high, which causes them to lose all of their work inputting data into a form.
I did come up with a solution, that involves adding a custom model binder for string properties that gets the value of the property without triggering the validations, and then testing it with a regular expression. A match on the regex then causes a new model state error to be added to that property.
public class MaliciousStringInputModelBinder : IModelBinder
{
private static readonly Regex maliciousStringPattern = new Regex(@"<[a-zA-Z0-9]");
private const string validationMessageFormat = "The {0} field contains potentially dangerous characters. Either remove the '<' characters, or ensure a space or puncuation character immediately follows them.";
public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
// Call extension method to safely get model value without
// triggering any validations
var valueResult = controllerContext.GetValueFromProvider(bindingContext);
if (valueResult == null || valueResult.AttemptedValue == null)
{
return null;
}
else if (valueResult.AttemptedValue == string.Empty)
{
return string.Empty;
}
else if (maliciousStringPattern.IsMatch(valueResult.AttemptedValue))
{
bindingContext.ModelState.AddModelError(bindingContext.ModelName, string.Format(validationMessageFormat, bindingContext.ModelMetadata.DisplayName));
}
bindingContext.ModelState.SetModelValue(bindingContext.ModelName, valueResult);
return valueResult.AttemptedValue;
}
}
This works, but feels like a hack. I'm not binding the model property, I'm validating it.
I've been looking into ASP.NET MVC action filters, but again I'm jumping through lots of hoops without a clear solution.
How can I add a universal form validation to all string view model properties site-wide in ASP.NET MVC to show a validation message to users instead of throwing an exception?