We are currently dealing with some XSS issues on one of our ASP.NET MVC projects. I found two issues - the first one has to do with our request validation pattern. The attacker could now use this security hole to drop some bad content in our database.
The second issue is how we display this content and we use the Html.DisplayTextFor method and it seems to be "broken".
Just create a new MVC 3 WebApp, put this in the HomeController:
public class HomeController : Controller
{
public ActionResult Index()
{
ViewBag.Message = "<SCRIPT/XSS SRC=\"htpp://ha.ckers.org/css.js\">";
User foo = new User();
foo.Name = "<SCRIPT/XSS SRC=\"htpp://ha.ckers.org/css.js\">";
return View(bla);
}
public ActionResult About()
{
return View();
}
}
public class User
{
public string Name { get; set; }
}
The View:
#Html.TextBoxFor(m => m.Name) <br/> ||| <-- will be encoded
#Html.Encode(ViewBag.Message)<br/> ||| <-- will be double encoded
#Model.Name <br/> ||| <-- will be encoded
#Html.DisplayTextFor(m => m.Name) <-- no encoding
<br/> |||
Output of the DisplayTextFor will be the whole string <script xss="" src="htpp://ha.ckers.org/css.js">
Question is: Bug, feature or am I using it wrong?
Html.DisplayTextFor is really for interacting with the [DisplayFormat] attribute (see MSDN).
So if you're using it with unsafe values, you have to be aware of this and use [DisplayFormat(HtmlEncode = true)] on your property.
Edit: Looks like the HtmlEncode property isn't actually enforced by DataAnnotationsModelMetadataProvider (and DisplayTextFor).
Related
I have a ViewModel that has a complex object as one of its members. The complex object has 4 properties (all strings). I'm trying to create a re-usable partial view where I can pass in the complex object and have it generate the html with html helpers for its properties. That's all working great. However, when I submit the form, the model binder isn't mapping the values back to the ViewModel's member so I don't get anything back on the server side. How can I read the values a user types into the html helpers for the complex object.
ViewModel
public class MyViewModel
{
public string SomeProperty { get; set; }
public MyComplexModel ComplexModel { get; set; }
}
MyComplexModel
public class MyComplexModel
{
public int id { get; set; }
public string Name { get; set; }
public string Address { get; set; }
....
}
Controller
public class MyController : Controller
{
public ActionResult Index()
{
MyViewModel model = new MyViewModel();
model.ComplexModel = new MyComplexModel();
model.ComplexModel.id = 15;
return View(model);
}
[HttpPost]
public ActionResult Index(MyViewModel model)
{
// model here never has my nested model populated in the partial view
return View(model);
}
}
View
#using(Html.BeginForm("Index", "MyController", FormMethod.Post))
{
....
#Html.Partial("MyPartialView", Model.ComplexModel)
}
Partial View
#model my.path.to.namespace.MyComplexModel
#Html.TextBoxFor(m => m.Name)
...
how can I bind this data on form submission so that the parent model contains the data entered on the web form from the partial view?
thanks
EDIT: I've figured out that I need to prepend "ComplexModel." to all of my control's names in the partial view (textboxes) so that it maps to the nested object, but I can't pass the ViewModel type to the partial view to get that extra layer because it needs to be generic to accept several ViewModel types. I could just rewrite the name attribute with javascript, but that seems overly ghetto to me. How else can I do this?
EDIT 2: I can statically set the name attribute with new { Name="ComplexModel.Name" } so I think I'm in business unless someone has a better method?
You can pass the prefix to the partial using
#Html.Partial("MyPartialView", Model.ComplexModel,
new ViewDataDictionary { TemplateInfo = new TemplateInfo { HtmlFieldPrefix = "ComplexModel" }})
which will perpend the prefix to you controls name attribute so that <input name="Name" ../> will become <input name="ComplexModel.Name" ../> and correctly bind to typeof MyViewModel on post back
Edit
To make it a little easier, you can encapsulate this in a html helper
public static MvcHtmlString PartialFor<TModel, TProperty>(this HtmlHelper<TModel> helper, Expression<Func<TModel, TProperty>> expression, string partialViewName)
{
string name = ExpressionHelper.GetExpressionText(expression);
object model = ModelMetadata.FromLambdaExpression(expression, helper.ViewData).Model;
var viewData = new ViewDataDictionary(helper.ViewData)
{
TemplateInfo = new System.Web.Mvc.TemplateInfo
{
HtmlFieldPrefix = string.IsNullOrEmpty(helper.ViewData.TemplateInfo.HtmlFieldPrefix) ?
name : $"{helper.ViewData.TemplateInfo.HtmlFieldPrefix}.{name}"
}
};
return helper.Partial(partialViewName, model, viewData);
}
and use it as
#Html.PartialFor(m => m.ComplexModel, "MyPartialView")
If you use tag helpers, the partial tag helper accepts a for attribute, which does what you expect.
<partial name="MyPartialView" for="ComplexModel" />
Using the for attribute, rather than the typical model attribute, will cause all of the form fields within the partial to be named with the ComplexModel. prefix.
You can try passing the ViewModel to the partial.
#model my.path.to.namespace.MyViewModel
#Html.TextBoxFor(m => m.ComplexModel.Name)
Edit
You can create a base model and push the complex model in there and pass the based model to the partial.
public class MyViewModel :BaseModel
{
public string SomeProperty { get; set; }
}
public class MyViewModel2 :BaseModel
{
public string SomeProperty2 { get; set; }
}
public class BaseModel
{
public MyComplexModel ComplexModel { get; set; }
}
public class MyComplexModel
{
public int id { get; set; }
public string Name { get; set; }
...
}
Then your partial will be like below :
#model my.path.to.namespace.BaseModel
#Html.TextBoxFor(m => m.ComplexModel.Name)
If this is not an acceptable solution, you may have to think in terms of overriding the model binder. You can read about that here.
I came across the same situation and with the help of such informative posts changed my partial code to have prefix on generated in input elements generated by partial view
I have used Html.partial helper giving partialview name and object of ModelType and an instance of ViewDataDictionary object with Html Field Prefix to constructor of Html.partial.
This results in GET request of "xyz url" of "Main view" and rendering partial view inside it with input elements generated with prefix e.g. earlier Name="Title" now becomes Name="MySubType.Title" in respective HTML element and same for rest of the form input elements.
The problem occurred when POST request is made to "xyz url", expecting the Form which is filled in gets saved in to my database. But the MVC Modelbinder didn't bind my POSTed model data with form values filled in and also ModelState is also lost. The model in viewdata was also coming to null.
Finally I tried to update model data in Posted form using TryUppdateModel method which takes model instance and html prefix which was passed earlier to partial view,and can see now model is bound with values and model state is also present.
Please let me know if this approach is fine or bit diversified!
just reading about UIHint from this url What is use of UIHint attribute in MVC
If you annotate a property with UIHint attribute
and use EditorFor or DisplayFor inside your views,
ASP.NET MVC framework will look for the specified
template which you specified through UIHintAttribute.
The directories it looks for is:
For EditorFor:
~/Views/Shared/EditorTemplates
~/Views/Controller_Name/EditorTemplates
For DisplayFor:
~/Views/Shared/DisplayTemplates
~/Views/Controller_Name/DisplayTemplates
the above write up means MVC engine first search view in shared if not found then it will search view in ~/Views/Controller_Name/DisplayTemplates ?
i just got a code but it is not complete so not being able to understand it properly
public class Person {
[UIHint("Poo")]
public string Name { get; set; }
}
#model MyApp.Models.Person
<h2>My Person</h2>
#Html.DisplayFor(m => m.Name)
if i think Poo is a shared view then where is poo related view code?
when this line will execute #Html.DisplayFor(m => m.Name) then what will happen.
see this code
#Html.EditorFor(model => model.ProductViewModel, "yourTemplateName")
where MVC will find the file yourTemplateName.cshtml?
thanks
the above write up means MVC engine first search view in shared if not found then it will search view in ~/Views/Controller_Name/DisplayTemplates ?
That is backwards, the search pattern is (exactly):
(if in an area)
"~/Areas/{2}/Views/{1}/DisplayTemplates/{0}.cshtml",
"~/Areas/{2}/Views/{1}/DisplayTemplates/{0}.vbhtml",
"~/Areas/{2}/Views/Shared/DisplayTemplates/{0}.cshtml",
"~/Areas/{2}/Views/Shared/DisplayTemplates/{0}.vbhtml"
then
"~/Views/{1}/DisplayTemplates/{0}.cshtml",
"~/Views/{1}/DisplayTemplates/{0}.vbhtml",
"~/Views/Shared/DisplayTemplates/{0}.cshtml",
"~/Views/Shared/DisplayTemplates/{0}.vbhtml"
Where
0 = Template/Type name
1 = ControllerName
2 = AreaName
(In the event you do not provide a Template name hint, the razor engine default to the type (int, boolean, string and even custom class types you've defined)
if i think Poo is a shared view then where is poo related view code?
In one more more of the locations above. This allows you to create poo specific views per controller and/or a shared poo view. It's however you want to do it.
when this line will execute #Html.DisplayFor(m => m.Name) then what will happen.
The engine will search the above folders for a template. In the event one is not found it then looks for object.cshtml/vbhtml in the same folders. If that file is found it executes it, if not it executes the default internal object display for code.
where MVC will find the file yourTemplateName.cshtml?
In the same directories above. You have to understand this it does the same thing over and over, it is a convention of asp.net-mvc.
What is use of UIHint attribute in ASP.Net MVC
This allows you to override the template used for a given property.
public class Person
{
[UIHint("Age")]
public DateTime Birthday { get; set; }
}
Will attempt to look for 'age.cshtml' in the above locations. Since the UIHintAttribute is not sealed you can also derive your own attribute and create some pretty nifty templates:
public UIDateTimeAttribute : UIHintAttribute
{
public UIDateTimeAttribute(bool canShowSeconds)
: base("UIDateTime", "MVC")
{
CanShowSeconds = canShowSeconds;
}
public bool CanShowSeconds { get; private set; }
}
Then your model might looks like:
public class Person
{
[UIDateTime(false)]
public DateTime Birthday { get; set; }
}
UIDateTime.cshtml
#model DateTime
#{
var format = "dd-MM-yy hh:mm:ss";
// Get the container model (Person for example)
var attribute = ViewData.ModelMetadata.ContainerType
// Get the property we are displaying for (Birthday)
.GetProperty(ViewData.ModelMetadata.PropertyName)
// Get all attributes of type UIDateTimeAttribute
.GetCustomAttributes(typeof(UIDateTimeAttribute))
// Cast the result as UIDateTimeAttribute
.Select(a => a as UIDateTimeAttribute)
// Get the first one or null
.FirstOrDefault(a => a != null);
if (attribute != null && !attribute.CanShowTime)
{
format = "dd-MM-yy hh:mm";
}
}
#Model.ToString(format)
I've searched all the available tutorials I can find, and I'm still having trouble with Umbraco Surface Controllers. I've created a bare-bones Surface Controller example which sorta works, but has some issues. Here's my code so far, questions to follow:
ContactformModel1.cs:
public class ContactFormModel1
{
public string Email { get; set; }
public string Name { get; set; }
public string HoneyPot { get; set; }
public string Title { get; set; }
public string Last { get; set; }
public string First { get; set; }
public string Addr { get; set; }
public string Phone { get; set; }
public string Time { get; set; }
public string Comment { get; set; }
}
ContactSurfaceController.cs:
public class ContactSurfaceController : Umbraco.Web.Mvc.SurfaceController
{
public ActionResult Index()
{
return Content("this is some test content...");
}
[HttpGet]
[ActionName("ContactForm")]
public ActionResult ContactFormGet(ContactFormModel1 model)
{
return PartialView("~/Views/ContactSurface/Contact1.cshtml", model);
}
[HttpPost]
[ActionName("ContactForm")]
public ActionResult ContactFormPost(ContactFormModel1 model)
{
// Return the form, just append some exclamation points to the email address
model.Email += "!!!!";
return ContactFormGet(model);
}
public ActionResult SayOK(ContactFormModel1 model)
{
return Content("OK");
}
}
Contact.cshtml:
#model ContactFormModel1
#using (Html.BeginUmbracoForm<ContactSurfaceController>("ContactForm"))
{
#Html.EditorFor(x => Model)
<input type="submit" />
}
ContactMacroPartial.cshtml:
#inherits Umbraco.Web.Macros.PartialViewMacroPage
#Html.Action("ContactForm", "ContactSurface")
My Questions:
I'm pretty sure that return ContactFormGet(model) is wrong in the
ContactFormPost method, but everything else I've tried throws an error.
When I try return RedirectToCurrentUmbracoPage(), I get Cannot
find the Umbraco route definition in the route values, the request
must be made in the context of an Umbraco request.
When I try return CurrentUmbracoPage(), I get Can only use
UmbracoPageResult in the context of an Http POST when using a
SurfaceController form.
The routing appears to work correctly (when I put a breakpoint inside ContactFormPost, the debugger stops there). But when the form comes back, I get the exact values I submitted. I don't see the !!! appended to the email address. (Note, this bit of code is just for debugging, it's not meant to do anything useful).
How do I call the "SayOK" method in the controller? When I change the BeginUmbracoForm method to point to SayOK, I still get stuck in the ContactFormPost method.
I'm sure I'm missing something incredibly stupid, but I can't figure this out for the life of me.
I wanted to take a moment to say how I resolved this. After playing around some more, I realized that I didn't really state my problem clearly. Basically, all I'm trying to do is embed an MVC form inside a Partial View Macro, so that it could be used in the content of a page (not embedded in the template).
I could get this solution to work, but I really didn't like how much logic the author put inside the View file. So I adapted his solution this way:
Partial View Macro (cshtml) file:
#inherits Umbraco.Web.Macros.PartialViewMacroPage
#using Intrepiware.Models
#{
bool isPostback = !String.IsNullOrEmpty(Request.Form["submit-button"]);
if(isPostback)
{
#Html.Action("CreateComment", "ContactSurface", Request.Form)
}
else
{
#Html.Partial("~/Views/Partials/ContactForm.cshtml", new ContactFormModel())
}
}
Form Partial View (cshtml) file:
#using Intrepiware.Models
#using Intrepiware.Controllers
#model ContactFormModel
<p>
<span style="color: red;">#TempData["Errors"]</span>
</p>
<p>
#TempData["Success"]
</p>
<div id="cp_contact_form">
#using(Html.BeginUmbracoForm("CreateComment", "BlogPostSurface"))
{
#* Form code goes here *#
}
ContactSurfaceController.cs file:
public class ContactSurfaceController : Umbraco.Web.Mvc.SurfaceController
{
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult ubCreateComment(ContactFormModel model)
{
if (processComment(model) == false)
return CurrentUmbracoPage();
else
return RedirectToCurrentUmbracoPage();
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult CreateComment(ContactFormModel model)
{
if(processComment(model) == true)
{
TempData["Success"] = "Thank you for your interest. We will be in contact with you shortly.";
ModelState.Clear();
}
return PartialView("~/Views/Partials/ContactForm.cshtml");
}
private bool processComment(ContactFormModel model)
{
// Handle the model validation and processing; return true if success
}
}
The controller is designed so that the form can be embedded either in the template or a Partial View Macro. If it's embedded in a template, the form should post to ubCreateComment; if it's in a macro, post to CreateComment.
I'm almost positive there's a better/more correct way of doing this, but I ran out of time to work on the project. If someone has a better solution, please post it!
One final question/note: You'll notice that the partial view macro posts Request.Form to the ContactSurfaceController.CreateComment, and MVC magically serializes it for me. That's safe, yeah? If so, doesn't MVC rock? :)
You are using a ChildAction because you are specifying #Html.Action("ContactForm", "ContactSurface") and because of this, in your View you need to:
Use Html.BeginForm(...) and not 'Html.BeginUmbracoForm(...)'
Allow the form to post back to the same path and not to the action
If you do this, then the form will post back to itself as expected.
See the documentation here for further help.
Edit:
Just saw the final part to your question. If you intend SayOK to be your 'thank you' message, I would just call it from your HttpPost action instead of returning the initial view.
My strongly typed View inherits from a "Person" object that is created with Linq to SQL. In my "Edit" View, i have to display of course old values:
<%= Html.TextBox("FirstName") %>
"FirstName" is NCHAR, so it need to be trimmed. So i ended up with:
<%= Html.TextBox("FirstName", Model.FirstName.Trim()) %>
and this works. But when form is submitted (after POST) and some errors occur, i need to show it again:
[AcceptVerbsAttribute(HttpVerbs.Post), Authorize(Roles = "office"), HandleError]
public ActionResult Edit(Models.Person person)
{
if (!(_personService.ValidatePerson(person))) // Persona non valida
{ return View(person); }
}
If for some reason the user left the textbox "FirstName" blank, the resulting property Person.FirstName become null and Model.FirstName.Trim() throws an Exception (Object reference not set to an instance of an object).
Any way to modify the bind and have all string trimmed by default? Or any ideas to how fix this?
Update: seems confirmed to be a MVC 2 behaviour.. still looking for a good way to handle this. Actually using an extension method:
public static string TrimOrDefault(this string value)
{
return value != null ? value.Trim() : string.Empty;
}
Something is odd here.
Model.FirstName shouldn't be null; the model binder is smart enough to set an empty input field ( textbox ) to "". Make sure your property names match up between your model and the textboxes your using.
Which version of MVC are you using? 1 or 2? I'm running a MVC 1 version in VS 2008 and the only way I can get FirstName to be null is by not including it in the form at all.
I could see if you initial GET Edit view threw this error and you had FirstName set to nullable in your dbml and database but since it is a Post this doesn't make sense to me right now. ;)
Update:
I've confirmed this:
With an empty form:
VS 2008 - Mvc 1 - FirstName = ""
VS 2010 - Mvc 2 - FirstName = null
Uh oh... Thats going to break a lot of code...
The Code:
View ( same for both ):
<% using (Html.BeginForm()) {%>
<fieldset>
<legend>Fields</legend>
<p>
<label for="FirstName">FirstName:</label>
<%= Html.TextBox("FirstName", Model.FirstName) %>
<%= Html.ValidationMessage("FirstName", "*") %>
</p>
<p>
<label for="LastName">LastName:</label>
<%= Html.TextBox("LastName", Model.LastName) %>
<%= Html.ValidationMessage("LastName", "*") %>
</p>
<p>
<input type="submit" value="Save" />
</p>
</fieldset>
<% } %>
VS 2010 - Mvc 2
public class HomeController : Controller
{
public ActionResult Index()
{
ViewData["Message"] = "Welcome to ASP.NET MVC!";
return View();
}
public ActionResult About()
{
return View();
}
public ActionResult AddPerson()
{
var person = new Person();
return View(person);
}
[HttpPost]
public ActionResult AddPerson(Person person)
{
return View(person);
}
}
public class Person
{
public string FirstName { get; set; }
public string LastName { get; set; }
}
VS 2008 - Mvc 1
public class HomeController : Controller
{
public ActionResult Index()
{
ViewData["Message"] = "Welcome to ASP.NET MVC!";
return View();
}
public ActionResult About()
{
return View();
}
public ActionResult AddPerson()
{
var person = new Person();
return View(person);
}
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult AddPerson( Person person )
{
return View(person);
}
}
public class Person
{
public string FirstName { get; set; }
public string LastName { get; set; }
}
No clean fix atm. The model binder is actually setting those properties to null. Set First and Last to "" in the constructor and exclude those properties from binding: [Bind(Exclude="FirstName, LastName")] they stay "".
Is this documented someplace?
Try a custom modelbinder.
See this : ASP.NET MVC: Best way to trim strings after data entry. Should I create a custom model binder?
In Models.Person have the default value be String.Empty which should elminate the null error at least.
If you want to have it automated, then you can add that into your dbml file. There is a designer.cs file attached and each field has a getter and setter. you can add code in there if you wish.
you could also create a partial class based on your table and handle the trim within that. it means that if you make a change to the dbml file then you don't lose your changes.
if you're not using a dbml file then let us know.
EDIT
If you have a class called person in your dbml file then this will already be declared as partial.
create another class, in the same project and do something like the following;
public partial class Person
{
public string NewName
{
get { return this._name.Trim(); }
}
}
So from then on in you can use .NewName instead of name and it will come back trimmed. You can also add code in there to ensure it's not null, not red, not whatever and return the appropriate value.
I'm not aware of an extension method that does this, if someone else does then please let me know.
I create a simple HTML helper for fields that COULD be null.
public static string TrimOrDefault(string value)
{
return (value == null ? "" : value.Trim());
}
And then in your code you can use
<%= Html.TextBox("FirstName", Helpers.TrimOrDefault(Model.FirstName)) %>
It's re-usable for future nullable fields and reads easily.
I've been playing with ASP.NET MVC and ran into something I can't figure out.
Suppose I have an object like this :
public class TestObject
{
public string Name { get; set; }
public int Age { get; set; }
}
And a view page (Create.aspx) like this :
<form action="/Create" method="post">
<p>
<%=Html.TextBox("Name") %>
</p>
<p>
<%=Html.TextBox("Age")%>
</p>
</form>
And on my controller I have these actions :
[AcceptVerbs(HttpVerbs.Get)]
public ActionResult Create()
{
return View(new TestObject { Name = "DefaultName", Age = 10 } );
}
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Create(TestObject o)
{
o.Name = "ChangedNameToSomethingElse";
o.Age = 15;
return View(o);
}
The Html.TextBox() method always genereates the textboxes with the default values, even after the postback, where the object is passed back with different properties on its values. Now, granted, I can't think of a real world example why I'd want to do such a thing but I still don't understand why I always end up having textboxes populated with the model's values that were set on the Create action with the AcceptVerbs(HttpVerbs.Get) attribute.
Note : I've tried Html.TextBox("Name", Model.Name) but the result is still the same. And I verified that the Create action with AcceptVerbs(HttpVerbs.Post) actually runs, by passing a value via ViewData to the View.
Also, the udated value is displayed when I output the value with <%=Model.Name %> but again, not on the textbox.
Is there something obvious I'm missing, or is there a reasoning behind this behaviour?
If you bind the result of a post request through the declaration of the method or by UpdateModel or TryUpdateModel to an object such as TestObject, a property called ModelState will get filled in with these values. The HTML helpers such as Textbox will always bind to modelstate over an explicitly passed model object.
I know this was answered a long time ago, but this is more targeted solution that works for me.
[HttpPost]
public ActionResult Create(TestObject o) {
ModelState.Remove("Name");
o.Name = "ChangedNameToSomethingElse";
ModelState.Remove("Age");
o.Age = 15;
return View(o);
}
Hope this helps someone out there.
Try this one:- (hope it will work for you)
[HttpPost]
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Create(TestObject o) {
ModelState.Clear();
o.Name = "ChangedNameToSomethingElse";
o.Age = 15;
return View(o);
}