Extended HandleError Attribute - status code 112031 (connection reset) - asp.net-mvc

I created a new class which inherits from the HandleErrorAttribute to handle ajax requests differently. The only override is on the OnException method:
public override void OnException(ExceptionContext filterContext)
{
if (filterContext.RequestContext.HttpContext.Request.IsAjaxRequest())
{
filterContext.ExceptionHandled = true;
filterContext.HttpContext.Response.Clear();
filterContext.HttpContext.Response.StatusCode = 500;
filterContext.HttpContext.Response.TrySkipIisCustomErrors = true;
filterContext.Result = new JsonResult { Data = errorText };
return;
}
base.OnException(filterContext);
}
This does work, about half the time. When it works, the status code in the response is 500 and the error message is provided. When it doesn't, the status code is 12031 and the error message is empty.
Apparently status code 12031 is means:
ERROR_INTERNET_CONNECTION_RESET
The connection with the server has been reset.
No idea why this would be occurring.

I'm not sure precisely what is going on here, but I can make an educated guess. We do something very similar, except we call base.OnException first, and then we test filterContext.ExceptionHandled to see if the base filter handled the exception. This has always worked for us, and is different than what you do. Note that the base filter will not always handle the exception (see the source code for details).
Note that the base filter does things which your filter does not do, like test IsCustomErrorEnabled and the specific type of the exception. There are some exceptions which should not be handled; again, refer to the MVC source code for details on this.
So my wild guess is that in some circumstances your filter (which always handles the exception when there is an AJAX request) and the base filter do different things. I would suggest that you try the method we are using (call base first, and only perform your custom AJAX handling if the request is an AJAX request and the base has indicated that the exception should be handled) and see if that doesn't end up working better for you.

Related

ValidateInputAttribute not working in Post Request of ASP.NET MVC controller

My understanding was OOTB, MVC will validate input to prevent XSS Attack and SQL Injection.
For example, In one of my app, the "a dangerous input has been detected" error will be received when I put in HTTP Get request. However, the post actions can let these values posted successfully through html input element without error. Even after I marked the controller action as [ValidateInput(true)]. How can I make them validate those post input?
Any advice will be appreciated!
Without seeing your GET handler, or what you're sending to it, it's tough to say why it behaves that way. However, OOTB MVC guards against SQL injection through the use of Entity Framework, and against XSS through ModelState validation.
Inside the body of your POST action that handles this forms submission you'll want to use code much like the following:
if (ModelState.IsValid)
{
//do the stuff I want to do when things are valid and free of XSS
}
else
{
//something went wrong. Probably shouldn't process this one. Have the user try again
}
Update: please disregard my filthy lies. ValidateInput(true) is not necessary because it is on by default. So, the only things I can think of would be that you have the AllowHtml attribute on your class or properties, or you are not posting back a model for modelBinding, and therefore input validation, to occur. At this point, you're probably going to need to put up some code for further help. There's too many unknowns right now.
I ran into a similar issue - 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
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, you can just decorate relevant MVC actions expecting JSON-posted data that might bypass the standard XSS prevention:
[HttpPost]
[ValidateJsonXss]
public ActionResult PublishRecord(RecordViewModel vm) { ... }
You can see other options for customizing request validation with OWASP .NET recommendations by 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

How do I handle OperationCanceledException in ASP.NET Web APIs?

I have this simple controller, whose Get method is called with ajax to look up zipcodes via an Entity Framework repository.
[Authorize]
public class ZipCodesApiController : AppApiController
{
public ZipCode Get(string zipCode)
{
return unitOfWork.ZipCodeRepository
.Get(x => x.Zip == zipCode)
.FirstOrDefault();
}
}
In production, my logs show that System.OperationCanceledException: The operation was canceled. is thrown quite often. I think what's going on is that users are viewing an address detail page, but navigating away or closing their browser before the ajax zipcode lookup returns. I guess IIS is telling my controller that they are no longer connected, and the .NET framework throws an exception?
This seems harmless, but it also seems like a bad idea to wrap the call to ZipCodeRepository in a try and have an empty OperationCanceledException catch clause.
I've googled the error and it seems to come up quite a bit in parallel programming, which is not something I am particularly familiar with.
What is an appropriate way to handle this exception? I think it's safe to ignore, but am I wrong about that, and the Entity Framework should be alerted so that it can clean something up (my AppApiController does have a dispose method at least)?
I get the same exceptions in my web API application, however i can catch them with the Application_Error method in Global.asax.cs
protected void Application_Error(object sender, EventArgs e)
{
Exception exception = Server.GetLastError();
Response.Clear();
OperationCanceledException httpException = exception as OperationCanceledException;
if (httpException != null)
{
var token = httpException.CancellationToken;
if (token.IsCancellationRequested)
{
// clear error on server
Server.ClearError();
Request.Abort();
}
}
}
I don't know if that is right.

How to get rid of that HTML error report in ASP.NET MVC?

All I need would be just the error message in plain text. But ASP.NET is doing some HTML report output from every error.
I have a jquery ajax call and when an error is thrown I'm getting all that crap over to the client side.
I've created a filter attribute but didn't helped.
public class ClientErrorHandler : FilterAttribute, IExceptionFilter
{
public void OnException(ExceptionContext filterContext)
{
var responce = filterContext.RequestContext.HttpContext.Response;
responce.Write(filterContext.Exception.Message);
responce.ContentType = MediaTypeNames.Text.Plain;
filterContext.ExceptionHandled = true;
}
}
EDIT
I'm seeing this
and I'd like to see just what is in here filterContext.Exception.Message
It looks to me like the reason why you cannot correctly handle the exception is because it happens outside of the MVC pipeline. If you look at the stack trace in the code you posted there is no reference to System.Web.Mvc code (the firing of exception filters when an exception occurs is called from ControllerActionInvoker.InvokeAction).
The stack trace indicates that the exception happens late in the ASP.NET pipeline (OnEndRequest) and that it's coming through the Autofac component.
To capture this error you would have to subscribe to the HttpApplication's Error event. See the following article on creating a global error handler: http://msdn.microsoft.com/en-us/library/994a1482.aspx . In this event you can handle the error and redirect to a custom error page.
you need to return a ContentResult
ContentResult result = new ContentResult();
result.Content = filterContext.Exception.Message;
result.ContentType = MediaTypeNames.Text.Plain;
filterContext.Result = result;
filterContext.ExceptionHandled = true;
Since you're using JQuery and WCF (by the details of your error), you might want to take a look at this article on how to handle service faults elegantly between jQuery and WCF - you might have to rework your service if you are able to do so.

ASP.NET MVC app custom error pages not displaying in shared hosting environment

I'm having an issue with custom errors on an ASP.NET MVC app I've deployed on my shared host. I've created an ErrorController and added the following code to Global.asax to catch unhandled exceptions, log them, and then transfer control to the ErrorController to display custom errors. This code is taken from here:
protected void Application_Error(object sender, EventArgs e)
{
Exception ex = Server.GetLastError();
Response.Clear();
HttpException httpEx = ex as HttpException;
RouteData routeData = new RouteData();
routeData.Values.Add("controller", "Error");
if (httpEx == null)
{
routeData.Values.Add("action", "Index");
}
else
{
switch (httpEx.GetHttpCode())
{
case 404:
routeData.Values.Add("action", "HttpError404");
break;
case 500:
routeData.Values.Add("action", "HttpError500");
break;
case 503:
routeData.Values.Add("action", "HttpError503");
break;
default:
routeData.Values.Add("action", "Index");
break;
}
}
ExceptionLogger.LogException(ex); // <- This is working. Errors get logged
routeData.Values.Add("error", ex);
Server.ClearError();
IController controller = new ErrorController();
// The next line doesn't seem to be working
controller.Execute(new RequestContext(new HttpContextWrapper(Context), routeData));
}
Application_Error is definitely firing because the logging works fine, but instead of displaying my custom error pages, I get the Go Daddy generic ones. From the title of the blog post the above code is taken from, I notice that it uses Release Candidate 2 of the MVC framework. Did something change in 1.0 that makes the last line of code not work? As usual it works great on my machine.
Any suggestions will be greatly appreciated.
Edit: Forgot to mention that I've tried all 3 possiblities for the customErrors mode in Web.config (Off, On, and RemoteOnly). Same results regardless of this setting.
Edit 2: And I've also tried it with and without the [HandleError] decoration on the Controller classes.
Update: I've figured out and fixed the 404s. There is a section of the Settings panel in Go Daddy's Hosting Control Center where 404 behavior can be controlled and the default is to show their generic page, and apparently this overrides any Web.config settings. So my custom 404 page is now showing as intended. However, 500s and 503s are still not working. I've got code in the HomeController to grab a static text version of the content if Sql Server throws an exception as follows:
public ActionResult Index()
{
CcmDataClassesDataContext dc = new CcmDataClassesDataContext();
// This might generate an exception which will be handled in the OnException override
HomeContent hc = dc.HomeContents.GetCurrentContent();
ViewData["bodyId"] = "home";
return View(hc);
}
protected override void OnException(ExceptionContext filterContext)
{
// Only concerned here with SqlExceptions so an HTTP 503 message can
// be displayed in the Home View. All others will bubble up to the
// Global.asax.cs and be handled/logged there.
System.Data.SqlClient.SqlException sqlEx =
filterContext.Exception as System.Data.SqlClient.SqlException;
if (sqlEx != null)
{
try
{
ExceptionLogger.LogException(sqlEx);
}
catch
{
// couldn't log exception, continue without crashing
}
ViewData["bodyId"] = "home";
filterContext.ExceptionHandled = true;
HomeContent hc = ContentHelper.GetStaticContent();
if (hc == null)
{
// Couldn't get static content. Display friendly message on Home View.
Response.StatusCode = 503;
this.View("ContentError").ExecuteResult(this.ControllerContext);
}
else
{
// Pass the static content to the regular Home View
this.View("Index", hc).ExecuteResult(this.ControllerContext);
}
}
}
Here's the code that attempts to fetch the static content:
public static HomeContent GetStaticContent()
{
HomeContent hc;
try
{
string path = Configuration.CcmConfigSection.Config.Content.PathToStaticContent;
string fileText = File.ReadAllText(path);
string regex = #"^[^#]([^\r\n]*)";
MatchCollection matches = Regex.Matches(fileText, regex, RegexOptions.Multiline);
hc = new HomeContent
{
ID = Convert.ToInt32(matches[0].Value),
Title = matches[1].Value,
DateAdded = DateTime.Parse(matches[2].Value),
Body = matches[3].Value,
IsCurrent = true
};
}
catch (Exception ex)
{
try
{
ExceptionLogger.LogException(ex);
}
catch
{
// couldn't log exception, continue without crashing
}
hc = null;
}
return hc;
}
I've verified that if I change the connection string to generate a SqlException, the code properly logs the error and then grabs and displays the static content. But if I also change the path to the static text file in Web.config to test the 503 version of the Home View, what I get instead is a page with nothing other than "service unavailable". That's it. No custom 503 message with the look and feel of the site.
Does anyone have any suggestions on improvements to the code that might help? Would it help to add different headers to the HttpResponse? Or is Go Daddy heavy-handedly hijacking the 503s?
I've found the solution and it's incredibly simple. Turns out the problem was actually in IIS7. While debugging this issue in Visual Studio I saw a property of the HttpResponse object that I hadn't noticed before:
public bool TrySkipIisCustomErrors { get; set; }
This lead me to my nearest search engine which turned up a great blog post by Rick Strahl and another on angrypets.com as well as this question here on SO. These links explain the gory details much better than I can, but this quote from Rick's post captures it pretty well:
The real confusion here occurs because the error is trapped by
ASP.NET, but then ultimately still handled by IIS which looks at the
500 status code and returns the stock IIS error page.
It also seems this behavior is specific to IIS7 in Integrated mode. From msdn:
When running in Classic mode in IIS 7.0 the TrySkipIisCustomErrors
property default value is true. When running in Integrated mode, the
TrySkipIisCustomErrors property default value is false.
So essentially all I ended up having to do is add Response.TrySkipIisCustomErrors = true; right after any code that sets the Response.StatusCode to 500 or 503 and everything now functions as designed.
I host an ASP.NET MVC site on GoDaddy and also faced issues dealing with custom error pages. What I found, through trial and error, was that GoDaddy intercepts errors at the HTTP level.
For example, any page which returned an HTTP status code of 404 caused GoDaddy's custom error page to take over. Eventually I changed my custom error pages to return 200 status and the 404-related problem went away. My HTML was the same, just the HTTP status needed to change.
I admittedly never tried doing the same with 503 status responses, but it's possible that the same mitigation may work. If you change from returning a 503 status to returning 200 status, does the problem go away?
Note that, if you do this workaround, you'll want to prevent search engines from indexing your error pages, which once then return a 200 status will be indistinguishable (from the search engine's perspective) from a regular page. So make sure to add a META ROBOTS tag to prevent indexing of your error pages, e.g.
<META NAME="ROBOTS" CONTENT="NOINDEX">
The downside of this approach may be that your page might be removed from Google, which is definitely not a good thing!
UPDATE: So, in addition, you can also detect whether the user agent is a crawler or not, and if it's a crawler return a 503 while if it's not a crawler, return a 200. See this blog post for info about how to detect crawlers. Yes, I know that returning different content to crawlers vs. users is an SEO no-no, but I've done this on several sites with no ill effect so far, so I'm not sure how much of a problem that is.
Doing both approaches (META ROBOTS and bot detection) may be your best bet, in case any oddball crawlers slip through the bot detector.

How can send back my own 404 error message in ASP.NET , but as json?

i'm trying to send back a simple error message as Json, with the HTTP code as 404.
So i started out writing my own IExceptionFilter that checks to see the exception. To keep this simple, if the exception throw is of type ResourceNotFoundException then i set the code to 404. Otherwise everything else if 500.
Now, the problem is .. the default IIS7 404 error message is returned :( my code is called .. but it seems to bypass it (later on in the pipeline)...
is there some trick i need to do?
do I need a custom error handling (in the web config) to be turned on or something?
Edit:
I'm trying to do what twitter does. Their Http Response Code documentation shows / explains some examples how they handle 404's, etc.. and i'm wanting to do that in my MVC app.
Edit 2:
The code i've done is listed here, for anyones reference :)
When you are handling your exception, are you setting ExceptionHandled to true?
Here's a quick example...
HandleException(ActionExecutedContext filterContext)
{
Exception exception = filterContext.Exception;
//Check if our exception has been handled.
if (filterContext.ExceptionHandled == false)
{
//Do your exception stuff
filterContext.Result = YourExceptionMessageAsAnActionResult();
//Set it as null.
filterContext.ExceptionHandled = true;
filterContext.HttpContext.Response.Clear();
}
}

Resources