XSS validation from MVC action from JSON ajax post - c#

Is there a way to manually call the XSS detection from MVC, such as from a filter, in cases when json is posted to an MVC action (like $.ajax post from JQuery with json content)?
We are using MVC 5 and have no issues with standard MVC mechanisms for catching XSS ("potentially unsafe input detected"), but a few pieces of the app use $.ajax to post JSON ("application/json" content type) to an MVC action. In these situations, we noticed the XSS detection does not run and allows dangerous form input.
In our investigation and research of similar questions here, we found JsonValueProviderFactory within the default model binder does not have same call for this XSS security that is present on form submissions or input from query string.
While it is easy to sanitize the input on client side, we obviously need a server validation as well in order to throw a 5xx (possibly something I can wrap in a filter to share across actions that could be impacted) to force this dangerous code detection on json submitted input with default model binding if possible to avoid re-inventing the wheel or inserting html encoded bad input to our db.

I found a solution that works for my situation. In this case, we had JQuery using $.ajax to post JSON to the MVC action. The default model binder does not validate posted JSON allowing unsafe XSS to be posted against our action.
To solve this, I found the RequestValidator has a static method InvokeIsValidRequestString that allowed validating a specific string to detect XSS (since every solution I had found until now reinvented the wheel here and did own XSS check for white/black list). Then it was just a matter of limiting the action to the appropriate scenario, getting the posted JSON, and throwing the validation error if XSS was detected.
public class ValidateJsonXssAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
var request = filterContext.HttpContext?.Request;
if (request != null && "application/json".Equals(request.ContentType, StringComparison.OrdinalIgnoreCase))
{
if (request.ContentLength > 0 && request.Form.Count == 0) //
{
if (request.InputStream.Position > 0)
request.InputStream.Position = 0; // InputStream has already been read once from "ProcessRequest"
using (var reader = new StreamReader(request.InputStream))
{
var postedContent = reader.ReadToEnd(); // Get posted JSON content
var isValid = RequestValidator.Current.InvokeIsValidRequestString(HttpContext.Current, postedContent,
RequestValidationSource.Form, "postedJson", out var failureIndex); // Invoke XSS validation
if (!isValid) // Not valid, so throw request validation exception
throw new HttpRequestValidationException("Potentially unsafe input detected");
}
}
}
}
}
Then, I can just decorate relevant MVC actions expecting JSON-posted data that might bypass the standard XSS prevention:
[HttpPost]
[ValidateJsonXss]
public ActionResult PublishRecord(RecordViewModel vm) { ... }
I was fortunate to stumble on thet OWASP .NET recommendations in which it recommended extending the RequestValidator object, which exposes the string validation done by the ValidateInput automatically utilized by MVC for other scenarios of query string, form collection, and cookie values.
For more info: https://www.owasp.org/index.php/ASP.NET_Request_Validation
If anyone has other recommendations, I would love to see other approaches.

Related

Why does my input not get validated when I'm not utilizing razor Html helpers?

Say I have a simple web form that does not utilize razor html helpers/a model.
<form id="survey-form" action="#Url.Action("Submit", "Questionnaire")" method="post">
<textarea name="1" form="survey-form">
</form>
The above form gets submitted using
$("#survey-form").submit();
Which posts to the following test method on a controller
[HttpPost]
[ValidateAntiForgeryToken]
[ValidateInput(true)]
public ActionResult Submit()
{
NameValueCollection nvc = Request.Form;
return View();
}
<script>alert("xss!")</script> into my text input does not get validated by the controller. Where I would normally expect the "A potentially dangerous Request.Form was detected from the client ..." exception to be thrown.
The Method does not return any exceptions or messages and is allowed to run.
The MSDN documentation says
The HttpRequest class uses input validation flags to track whether to perform validation on the request collections accessed through the Cookies, Form, and QueryString properties.
Since I am posting and processing a posted form with potentially dangerous values I would expect my input to be validated.
Is it possible for me to still utilize a simple MVC helper like the ValidateInput attribute, to validate user input without using the ASP.NET MVC conventions? (eg. creating a form using razor html helpers and a model etc.)
Edit:
As there seems to be some confusion of the values actually reaching my Action on the Controller, here is a screenshot showcasing that the values do reach the function.
I think you should include FormCollection as an argument for the Submit ActionResult.

How can I safely handle POST parameters in an HTTP Handler using C#?

I'm working on an ASP.Net C# application (my first!) that contains an HTTP Handler within it. My application works with several parameters that are passed in via the URL. My question(s) is as follows:
My applications entry point is via the HTTP Handler. When I enter my ProcessRequest method, I am assigning the values of the URL parameters to variables so that I may do something with the data.
Question: Is this safe, even if I am not setting the value to anything when I call the URL?
In my example: I call host/handler1.ashx instead of host/handler1.ashx?something=foo
public void ProcessRequest(HttpContext context)
{
context.Response.ContentType = "text/plain";
string something = context.Request["something"];
context.Response.Write("Hello World: " + something);
}
When calling the above method using the plain URL with no parameters, it executes just fine, but the string something is just blank/null.
Additional questions:
What happens to the variable something in the case that I do not explicitly initialize it via the URL? I understand that it is null, but can this lead to problems?
Is it dangerous or not safe to call the plain URL (i.e. should I always call it with parameter values specified)?
What is the best way to call a "clean" ashx URL to start the application but not risk problems?
The application will do a series of subsequent GET redirects as it accumulates values and passes them back to the app via the query string.
Should I do a POST or GET upon initial call of the application?
Sorry for asking the same question multiple ways, but I'm a bit confused on the topic and this is my first time writing an app like this. Any patience and advice you could provide on how to safely handle and initialize parameters is greatly appreciated!
There is nothing wrong with omitting parameters to an endpoint. As the developer you are in charge of enforcing what the client is allowed send to you. If you expect a parameter and it's missing, throw an error (e.g. HttpException).
If you are creating or updating data (i.e. inserting or updating records in a database) the best method would be a POST or PUT.
Edit - Here is an example of how you can handle the input:
public void ProcessRequest(HttpContext context) {
//Maybe you require a value?
if (string.IsNullOrEmpty(context.Request["something"])) {
throw new HttpException(400, "You need to send a value!");
}
//Maybe you require a certain value?
if (context.Request["something"] != "beerIsGood") {
throw new HttpException(400, "You need to send the right value!");
}
}
You can't. The Internet is dangerous.

ASP.NET MVC Client-side validation with MvcContrib FluentHtml

What's the recommended way to do client-side validation using the built-in MVC2 code with MvcContrib's FluentHtml builders? We're using the jQuery client-side validation code, not the default Microsoft AJAX stuff, if that matters (though I don't think it should).
It seems the client-side validation only gets registered with jQuery Validate when you place a validation message (Html.ValidationMessageFor(x => x.FirstName)) on the page. MvcContrib's FluentHtml this.ValidationMessage(x => x.FirstName) only works with ModelState on the server side, doesn't write out any HTML if there's no error, and doesn't register the given property with jQuery Validate on the client-side.
So my question: is there a way to make the current trunk build of MvContrib work with MVC2's built-in client-side validation somewhat painlessly right now? If so, how? If not, is there another client-side validation that's recommended (other than xVal, which we're currently using and has been depreciated)? Should this be patched in MvcContrib so it works properly? A last resort would be to move to using ASP.NET MVC's built-in input builders, but we already invested a lot in MvcContrib's and would rather not.
Thanks!
Im in the exact same situation...i came across this post with in interesting comment further down although I couldn't quite get it to work.
http://lunaverse.wordpress.com/2008/11/24/mvcfluenthtml-fluent-html-interface-for-ms-mvc/
If you can make any sense of it would be good to post it back up here.
Paul
I got the comment from that blog article working Paul, and modified it to use all the known MVC validation adapters instead of just the Required one (basically mimicking much of what's in the framework itself). It gets kind of hairy with how it displays the error message and working with what we already have, and I implemented a patch for MVC Contrib to work with it, but in the end I'm giving up for now until MVC3 is finialized and MVC Contrib builds against it. No point in going through all this when there's an updated release coming soon.
Here's what I ended up with (FluentViewPage<T> is where we add behaviors):
public class ClientsideValidationBehavior<T> : IBehavior<IMemberElement> where T : class
{
private readonly FluentViewPage<T> _viewPage;
public ClientsideValidationBehavior(FluentViewPage<T> viewPage)
{
_viewPage = viewPage;
}
public void Execute(IMemberElement element)
{
var attribute = element.GetAttribute<ValidationAttribute>();
if (attribute == null)
{
return;
}
var formContext = _viewPage.ViewContext.FormContext;
var fieldMetadata = formContext.GetValidationMetadataForField(UiNameHelper.BuildNameFrom(element.ForMember), true);
var modelMetadata = ModelMetadata.FromStringExpression(element.ForMember.Member.Name, _viewPage.ViewData);
var validators = ModelValidatorProviders.Providers.GetValidators(modelMetadata, _viewPage.ViewContext);
validators.SelectMany(v => v.GetClientValidationRules()).ForEach(fieldMetadata.ValidationRules.Add);
fieldMetadata.ValidationMessageId = element.ForMember.Member.Name + "_Label";
}
}
Hope that helps some.

How can I pass JavaScript errors from an action to my view?

I have 2 actions, one for GET and the other handles POST requests.
Although I have validation on the client side, there are some things I check for on the server-side also (in my action method).
In the POST action, when I find errors etc. that I want to report back to the UI, what options do I have to send back messages/errors to the client side?
Note:
I am using the popular jQuery validation plugin als.
I you are using Model binding from your View to your Post Action, it has built in validation that can be used for this.
Some people however prefer to do validation outside of the Model. To return these errors back to the View, you need to update the ModelState, then do the normal check if it is valid (contains errors):
public Dictionary<string, string> ValidateEntry(Object obj)
{
var errors = new Dictionary<string, string>();
if (string.IsNullOrEmpty(obj.Owner))
{
errors.Add("Owner", "You must select Owned By.");
}
//... whatever else
return errors;
}
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Edit(Object cap)
{
var errors = ValidateEntry(cap);
if (errors.Count > 0)
{
foreach (var err in errors)
{
ModelState.AddModelError(err.Key, err.Value);
}
}
if (ModelState.IsValid)
{
//... do if valid
}
return View(ViewModel);
}
Some rules I go by when implementing validation in web applications/sites:
The validation should occur client side and server side
Server side validation should mirror the client side validation for users with scripting turned off
Even though the validation rules are enforced in two places the logic should not be duplicated
Going by those rules I use the following solution:
xVal (found from ScottGu's blog)
IDataError solution from Adam
Schroder
Business rules are implemented in the model with DataAnnotations
The service may throw custom exceptions for unique business constraints
Its amazing how easy this is to use and how well it works. It uses the JQuery validation plugin transparently and just does what it is supposed to with minimal coding.
Client side validation in your view consists of:
<%= Html.ClientSideValidation<Foo>() %>
Server side validation in your action consists of
ModelState.IsValid

MVC - Passing Data with RedirectToAction()

I'd like to take data entered in an MVC user form and display it in a different view.
The class has the following private variable:
IList<string> _pagecontent = new List<string>();
The following action accepts a FormCollection object, validates it, and passes it on to the "Preview" view as a List:
[Authorize(Roles = "Admins")]
[ValidateInput(false)]
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult UpdateContent(FormCollection collection)
{
if (ModelState.IsValid)
{
string PageToInsert = collection["PageToInsert"];
string PageHeader = collection["PageHeader"];
string PageBody = collection["PageBody"];
//validate, excluded...
_pagecontent.Add(PageToInsert);
_pagecontent.Add(PageHeader);
_pagecontent.Add(PageBody);
}
return RedirectToAction("Preview", _pagecontent);
}
The Preview view has the following Page Directive for passing a strongly typed object List:
<%# Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Template.Master" Inherits="System.Web.Mvc.ViewPage<List<string>>" %>
I would expect to be able to use the Model object to get my data, but alas I cannot. At the following line, I get an error index out of bounds exception, stating the index must be non-negative and less than the size of the collection:
<% if (Model[0].ToString() == "0") { %>
And some strange parameters have been added to the URL, as it resolves to
http://localhost:1894/Admin/Preview?Capacity=4&Count=3
So I have two questions:
When I call RedirectToAction and pass it my List, why is it inaccessible in the view's Model object?
What is the proper way to go about doing what I'm trying to do, namely pass a collection of strings to a view for display there?
Try using TempData. It is like a single-shot session object. You put values you want into TempData, immediately redirect and get them out. There is a good writeup here: http://blogs.teamb.com/craigstuntz/2009/01/23/37947/
Be careful when using TempData. It works great in a single server environment but in a cloud environment it may not work as expected since you cannot guarantee that the request will hit the same machine. This happens because TempData rely on the asp.net session. But if you are using other session manager like SQL or AppFabric Cache it will work fine.
The second parameter to RedirectAction is routeValues, not model.
protected internal RedirectToRouteResult RedirectToAction(string actionName, object routeValues);
Try using TempData for the model. Its for persisting data between redirects.
The problem with RedirectToAction is it's returning a HTTP 302 and the browser is then on it's own going and doing a brand new HTTP request. You may want to consider using a cookie and/or session object to persist the data between requests.
This is not working because RedirectToAction is actually sending back a Http 302 to the browser. When the browser receives this 302, it does a new request to the server asking for the new page. New request, new temp variables.
You will also face this problem when you try to save/edit/delete something and for some reason you deny it and you have to return the old form again.
So, instead of:
return RedirectToAction("Preview", _pagecontent);
Put the Preview logic in a separate method and just call it:
return PreviewLogic(_pagecontent);
You can also use the TempData[] dic to persist data for the next request like others have said, but then you will not avoid the 302 additional round trip to the server.
It sounds like you're trying to do:
public ActionResult UpdateContent(FormCollection form) {
...
return View("Preview", _pagecontent);
}
Note that a redirection is supposed to be a "clean slate" for the browser (except for things like the auth cookie). You don't get to tell the browser to pass information along to the next request, since the next request should be able to stand on its own. All you get to do is tell the browser what URL to request next. In ASP.NET MVC, when you pass an arguments-object to RedirectToAction, the public properties of that object are appended as query-string parameters to the generated URL.
Can't you just make 2 action results with the same name and mark 1 of them with HttpPost?
public ActionResult UpdateContent(FormCollection preview = null)
{
return View(preview);
}
[HttpPost]
public ActionResult UpdateContent(FormCollection collection = null, bool preview = false)
{
if (preview)
return UpdateContent(collection);
else
return UpdateContent(null);
}
It looks like you are looking for the UpdateModel command:
Check out ScottGu's blog post on the topic:
Improved UpdateModel and TryUpdateModel methods

Categories

Resources