Solving Potentially dangerous Request.Form with custom attribute in MVC3


If you punch in “<” character while filling a MVC web form (or even ASP.NET) and press submit, you’ll encounter a System.Web.HttpRequestValidationException exception. I.e.,
form
results in:
error
Upon search, you’ll find few common options to resolve this. I've tried to consolidate them and also have added an interesting approach at end which I find very useful for handling this issue:

Option-1: Disabling RequestValidation through Web.Config
Adding following tag in Web.Config:










Pros: Easiest to do!!
Cons: Skips validation throughout application. As a good rule, Validation should be explicitly bypassed but by setting this option, we are implicitly bypassing validation which is not good.


Option-2: ValidateInput attribute
Another option that resolves this problem is using ValidationInput = false attribute on Controller. For example:

[ValidateInput(false)]
[HttpPost]
public ActionResult Index(MyViewModel vModel)
{
return View();
}
Pros: Easy to use and can be applied only on specific controllers
Cons: Once applied to a controller, all values posted from form for this controller skip validation. Also, it is tedious when you are specifically trying to bypass form values for a model used in many controllers.

Option-3: AllowHTML
Using AllowHTML tag on Model field. For example, if our Model used to render form was:

public class UserModel
{
public string FirstName { get; set; }
}

Only change that needs to be done is adding AllowHTML attribute over FirstName.

[AllowHtml]
public string FirstName { get; set; }

Pros: Firstly it can be applied to a specific property (instead skipping validation at controller or application level). Secondly it is easy to use and also removes code redundancy as this attribute needs to be applied only once (over the property).
Cons:Problem with this approach becomes evident, when you plan to display content in raw format on a view. For example, if you set [AllowHTML] and then enter “<script>alert(1)</script>” as input value, there will be no error on form submission. :
< @Model.UserName will display <script>alert(1)</script> as it is. This is good on part of MVC that be default it is encoding HTML content before display.

But what, if you want to display HTML, as it is (may be because it is a rich text entry)?

If you instead use following code:

@Html.Raw(Model.UserName), and pass same input, you will see an alert because browser would execute the script.

alert

This is of course dangerous!!
You can resolve this issue by adding a regular expression validation too which checks for <script> tag in content. However this need to know regular expressions (I found less people comfortable in RegEx) and also bad from maintenance perspective as if there is a need tomorrow to disallow one more tag besides <script>, this would require updating of all fields having combo of AllowHTML and RegularExpression.
A much better approach is one which:


  • Allows HTML content

  • Can be customized to specify tags allowed

  • Is as easy to use and granular at property level like AllowHTML

  • Easy to maintain

One option that nicely fulfills above conditions is creating a custom attribute.



[AttributeUsage(AttributeTargets.All, Inherited = false, AllowMultiple = true)]
sealed class RichHTML : ValidationAttribute, IMetadataAware
{
public RichHTML()
: base("no script tags please..!")
{
}

public override bool IsValid(object value)
{
if (value.ToString().ToLower().Contains("<script>"))
{
return false;
}
return true;
}

public void OnMetadataCreated(ModelMetadata metadata)
{
metadata.RequestValidationEnabled = false;
}

}

This is enough for us to solve our problem. Following is an explanation:


  • First I’ve declared a class “RichHTML” decorated with attribute specification

  • I’ve inherited ValidationAttribute (found in System.ComponentModel.DataAnnotations.ValidationAttribute) which is a base class for all validation attributes.

  • I also implemented IMetadataAware interface. This provides an event handler OnMetadaCreated which is invoked quite early when model field is being constructed. Handler which throws HttpWebRequest exception, executes sometime after this.

  • I created a constructor which tells base that if an error comes, the message to display is “no script tags please..!”. You can instead set it by overriding ErrorMessage property

  • IsValid is overriden to check presence of “<script>” tag in content posted. If found IsValid is false otherwise true. If another tag too needs to be checked, only this method needs an update across application.

  • Finally and most importantly, I’ve set metadata.RequestValidationEnabled to false for this property. Setting this makes framework to bypass http request validation and prevents “Potentially dangerous Request.Form…” error.

You can now decorate your model with RichHTML instead of AllowHTML as following:

public class UserModel
{
[RichHTML]
public string FirstName { get; set; }
}
show

Comments

Popular posts from this blog

How to show only month and year fields in android Date-picker?

How to construct a B+ tree with example

Conflict Serializability in database