Reputation: 916
If I have a standard signup form with a text field, and I type script tags into it, MVC sends back an error saying that "A potentially dangerous Request.Form value was detected from the client". Is this just a debug error? Will my users see that on the live site?
Is there a safe and easy way to just silently accept and encode/sanitize input from a field such that MVC doesn't throw up a scary error, but the input is rendered harmless?
Obviously I'd also want to make sure my output is encoded, but Razor takes care of that for the most part, and I'm careful with @Html.Raw
. We are also already taking care of database inputs by using parameterized queries, so SQL injection shouldn't be a threat.
Upvotes: 2
Views: 12282
Reputation: 335
The "A potentially dangerous Request.Form value was detected from the client" exception is not just a debug error and is is an error that will propagate on the deployment server. So your users will see this error (or your custom error page) on the live server.
To allow for un-sanitized HTML to be successfully bound to your model you may do any of the following:
[AllowHtml]
attribute[ValidateInput( false )]
attributeIf you're using MVC v4.0 include the following in your web.config:
<location path="Accounts/SignUp">
<system.web>
<pages validateRequest="false" />
<httpRuntime requestValidationMode="2.0" />
</system.web>
</location>
I repeat, all of the above will allow for the un-sanitized HTML to be bound to your model. This means there will be no "A potentially dangerous Request.Form value was detected from the client" error but your input will NOT be sanitized and could still contain potentially harmful tags.
To allow for sanitized input to be bound to your model you may adapt the PropertyBindAttribute implementation from Customizing property binding through attributes to allow you to decorate your model properties with an [AllowSanitizedHtml]
attribute where you would like the functionality to allow for HTML input to be accepted after sanitization. For sanitization, you can use the Microsoft.Security.Application.Encoder.Encode()
helper method (NuGet package name: 'AntiXSS')
Firstly, an attribute to provide the base class for propery binding:
[AttributeUsage( AttributeTargets.Property , AllowMultiple = false )]
public abstract class AbstractPropertyBinderAttribute :
Attribute
{
public abstract bool BindProperty(
ControllerContext controllerContext ,
ModelBindingContext bindingContext ,
PropertyDescriptor propertyDescriptor
);
}
Then, the custom model binder that knows of this attribute so that model binding may handle the special case of an attribute property binding:
public class CustomModelBinder :
DefaultModelBinder
{
protected override void BindProperty(
ControllerContext controllerContext ,
ModelBindingContext bindingContext ,
PropertyDescriptor propertyDescriptor )
{
if( propertyDescriptor.Attributes.OfType<AbstractPropertyBinderAttribute>().Any() )
{
var modelBindAttr = propertyDescriptor.Attributes.OfType<AbstractPropertyBinderAttribute>().FirstOrDefault();
if( modelBindAttr.BindProperty(
controllerContext ,
bindingContext ,
propertyDescriptor ) )
{
return;
}
}
base.BindProperty(
controllerContext ,
bindingContext ,
propertyDescriptor
);
}
}
Don't forget to add the following code to your Global.asax to allow for the CustomModelBinder to be used as the default:
System.Web.Mvc.ModelBinders.Binders.DefaultBinder = new CustomModelBinder();
Finally, the AllowSanitizedHtmlAttribute
:
public class AllowSanitizedHtmlAttribute :
AbstractPropertyBinderAttribute ,
IMetadataAware
{
public override bool BindProperty(
ControllerContext controllerContext ,
ModelBindingContext bindingContext ,
PropertyDescriptor propertyDescriptor )
{
if( propertyDescriptor.PropertyType == typeof( string ) )
{
var unvalidatedValueProvider = bindingContext.ValueProvider as IUnvalidatedValueProvider;
var result = unvalidatedValueProvider.GetValue(
propertyDescriptor.Name ,
true
);
if( result != null )
{
propertyDescriptor.SetValue(
bindingContext.Model ,
Encoder.HtmlEncode( result.AttemptedValue )
);
return true;
}
}
return false;
}
#region IMetadataAware Members
public void OnMetadataCreated( ModelMetadata metadata )
{
if( metadata == null )
throw new ArgumentNullException( "metadata" );
metadata.RequestValidationEnabled = false;
}
#endregion
}
Now you can safetly decorate the model properties that require sanitization in your SignUp form with [AllowSanitizedHtml]
attributes. This will allow for the input to your model properties to be silently bound after being sanitized.
Upvotes: 5
Reputation: 30035
You can decorate your Action with the [ValidateInput(false)]
attribute. That will result in Request Validation being bypassed for any request that invokes the particular action.
Alternatively, you can decorate a specific property on your mode with the [AllowHtml]
attribute.
If you don't do this, and you don't provide a custom error page, then users will see some kind of error message if they attempt to pass HTML tags as part of a request. That could be in posted values, query string, URL or even in a cookie.
More information on MSDN here: http://msdn.microsoft.com/en-us/library/hh882339(v=vs.110).aspx
Upvotes: 0