Handle URI hacking gracefully in ASP.NET - asp.net

I've written an application that handles most exceptions gracefully, with the page's design intact and a pretty error message. My application catches them all in the Page_Error event and there adds the exception to HttpContext.Curent.Context.Items and then does a Server.Transfer to an Error.aspx page. I find this to be the only viable solution in ASP.NET as there seems to be no other way to do it in a centralized and generic manner.
I also handle the Application_Error and there I do some inspection on the exception that occurred to find out if I can handle it gracefully or not. Exceptions I've found I can handle gracefully are such that are thrown after someone hacking the URI to contain characters the .NET framework considers dangerous or basically just illegal at the file system level.
Such URIs can look like e.g.:
http://exmample.com/"illegal"
http://example.com/illegal"/
http://example.com/illegal /
(notice the space before the slash at the end of the last URI).
I'd like these URIs to respond with a "404 Not Found" and a friendly message as well as not causing any error report to be sent to avoid DDOS attack vectors and such. I have, however, not found an elegant way to catch these types of errors. What I do now is inspect the exception.TargetSite.Name property, and if it's equal to CheckInvalidPathChars, ValidatePath or CheckSuspiciousPhysicalPath, I consider it a "path validation exception" and respond with a 404.
This seems like a hack, though. First, the list of method names is probably not complete in any way and second, there's the possibility that these method names gets replaced or renamed down the line which will cause my code to break.
Does anyone have an idea how I can handle this less hard-coded and much more future-proof way?
PS: I'm using System.Web.Routing in my application to have clean and sensible URIs, if that is of any importance to any given solution.

It may be that System.Web.Routing supports some sort of url filtering, but it is quite easy to implement your own.
Look at the System.Web.IHttpModule interface and read about implementing custom HTTP Modules. Http modules enter that Asp.Net pipeline and run before your page is run. You can use it to perform logging of requests, to modify requests and in your case to filter requests. The Asp.Net routing module is also implemented as a custom HTTP Module.
What you can do is to implement a Http Module that looks at the requested url and check if it is valid. If the url is invalid you can do whatever you need, for example redirect it to your 404 - not found page or you can just stop the request.

I don't think using System.Web.IHttpModule is the correct answer for IIS7+. I am trying to implement IHttpModule to validate the path but the exception has been thrown before the HttpModule is executed.
This is my exception stack:
[ArgumentException: Illegal characters in path.]
System.IO.Path.CheckInvalidPathChars(String path) +7493413
System.IO.Path.Combine(String path1, String path2) +40
System.Web.Configuration.UserMapPath.GetPhysicalPathForPath(String path, VirtualDirectoryMapping mapping) +114
System.Web.Configuration.UserMapPath.GetPathConfigFilename(String siteID, VirtualPath path, String& directory, String& baseName) +72
System.Web.Configuration.UserMapPath.MapPath(String siteID, VirtualPath path) +30
System.Web.Configuration.UserMapPath.MapPath(String siteID, String path) +31
System.Web.Hosting.HostingEnvironment.MapPathActual(VirtualPath virtualPath, Boolean permitNull) +297
System.Web.Hosting.HostingEnvironment.MapPathInternal(VirtualPath virtualPath, Boolean permitNull) +51
System.Web.CachedPathData.GetConfigPathData(String configPath) +341
System.Web.CachedPathData.GetVirtualPathData(VirtualPath virtualPath, Boolean permitPathsOutsideApp) +110
System.Web.HttpContext.GetFilePathData() +36
System.Web.HttpContext.GetConfigurationPathData() +26
System.Web.Configuration.RuntimeConfig.GetConfig(HttpContext context) +43
System.Web.Configuration.CustomErrorsSection.GetSettings(HttpContext context, Boolean canThrow) +41
System.Web.HttpResponse.ReportRuntimeError(Exception e, Boolean canThrow, Boolean localExecute) +101
System.Web.HttpRuntime.FinishRequest(HttpWorkerRequest wr, HttpContext context, Exception e) +383
and this is the link to Application Life Cycle for IIS 7.0 (http://msdn.microsoft.com/en-us/library/bb470252.aspx)
I am guessing that the exception caused by the "RESOLVE CACHE" step

Writing Custom HttpModule didn't work for me - I still got the "Illegal characters in path" error, but answer to this question solved the problem:
Turns out you could avoid this by setting allowDoubleEscaping="false" in for requestFiltering in web.Config. I.e:
<configuration>
<system.webServer>
<security>
<requestFiltering allowDoubleEscaping="false" />
</security>
</system.webServer>
</configuration>
Perhaps not the perfect solution (any suggestions for a better one is much appreciated), but it solves the problem.

Related

Custom Error Page for 'Illegal Characters in path'

The custom error page I set in the web.config does not get displayed when an error of "Illegal Characters in path" is raised. It does work for other server errors, so I'm not sure if there is anything I can do?
Example Request that correctly shows Custom Error Page: website.com/3f.jsp
Example Request that does not: website.com/%3f.jsp
Web.config: <customErrors mode="On" defaultRedirect="ErrorDisplay.aspx"></customErrors>
Stacktrace (customErrors Off)
[ArgumentException: Illegal characters in path.]
System.Security.Permissions.FileIOPermission.HasIllegalCharacters(String[] str) +276
System.Security.Permissions.FileIOPermission.AddPathList(FileIOPermissionAccess access, String[] pathListOrig, Boolean checkForDuplicates, Boolean needFullPath, Boolean copyPathList) +88
System.Security.Permissions.FileIOPermission..ctor(FileIOPermissionAccess access, String[] pathList, Boolean checkForDuplicates, Boolean needFullPath) +43
System.IO.Path.GetFullPath(String path) +82
System.Web.HttpApplication.CheckSuspiciousPhysicalPath(String physicalPath) +19
System.Web.Configuration.HttpConfigurationSystem.ComposeConfig(String reqPath, IHttpMapPath configmap) +175
System.Web.HttpContext.GetCompleteConfigRecord(String reqpath, IHttpMapPath configmap) +434
System.Web.HttpContext.GetCompleteConfig() +49
System.Web.HttpContext.GetConfig(String name) +195
System.Web.CustomErrors.GetSettings(HttpContext context, Boolean canThrow) +20
System.Web.HttpResponse.ReportRuntimeError(Exception e, Boolean canThrow) +39
System.Web.HttpRuntime.FinishRequest(HttpWorkerRequest wr, HttpContext context, Exception e) +486
I tried to manually catch the error in the Global.asax Application_Error handler, but that never gets called. Application_BeginRequest doesn't even get called when there are illegal characters, so where is IIS throwing this error and is there a way for me to display a custom page?
According to this article I may be out of luck.
As your stack trace shows and you observed in Application_BeginRequest not getting called, this error occurs in IIS's request handling before your application code even gets called - the answer to your first question: therefore, your Web.config and Application_Error implementation are not considered.
A related discussion on ASP.NET forums indicates as much too.
On my local IIS (7.5) server, I get back...
Bad Request - Invalid URL
HTTP Error 400. The request URL is invalid.
...for the URL http://localhost/%&what.
Curious, I tried setting a static custom error page for HTTP status code 400 under my local server's Error Pages in IIS Manager, also configuring my local server to use custom error pages for local and remote requests; but I never saw the custom error page I set for this particular error.
A TechNet article explains why:
You cannot customize the following HTTP errors: 400, 403.9, 411, 414, 500, 500.11, 500.14, 500.15, 501, 503, and 505.
So I think the answer to your second question is, no, IIS does not provide a way for you to display a custom error page for this.

CSHTML rendering text only - static page?

This is a continuation of my previous question (.CSHTML pages will not render), but I am no longer getting a 500 error, thus the new post. My pages are now just rendering plain text / html (regardless of what I do).
I can get the pages to work correctly if I try to view them through WebMatrix3, but I cannot view them from the browser (either localhost or through the web).
I recently realized that my pages were set up for ASP.NET v2.0, which I am guessing does not support .cshtml. So, I changed everything to v4.0 but I still don't have any luck view the pages correctly. It's just plain text.
I have:
MVC 3 installed
IIS 7.5 on Win7 Home Premium
The dir of the pages that I want to load converted to application
web.config functioning, though I am not sure what else, if anything I need to have it in
My server functioning normally with HTML, .css, .php, python, etc... But I am having horrible luck with any ASP.NET functionality (this includes .aspx).
I really don't know what other information I need to put here, but if you ask for it, I shall provide it.
EDIT 1:
Now I am just getting 404 errors on any .cshtml page I try to view. This happened before when I didn't have the MIME types in, but was corrected (to at least plain text) when I entered the MIME type in. I have no idea what is going on... at this point I am almost ready to just uninstall everything and try to start over. =\
EDIT 2:
Okay, so I have gotten rid of my 404 and 500 errors. I ended up adding a privileged user to the application pool (advanced settings > process model > Identity). It was set as defaultAppPool before. Now I am getting this:
Type 'ASP._Page_default2_cshtml' does not inherit from 'System.Web.WebPages.WebPage'.
Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.
Exception Details: System.Web.HttpException: Type 'ASP._Page_default2_cshtml' does not inherit from 'System.Web.WebPages.WebPage'.
Source Error:
An unhandled exception was generated during the execution of the current web request. Information regarding the origin and location of the exception can be identified using the exception stack trace below.
Stack Trace:
[HttpException (0x80004005): Type 'ASP._Page_default2_cshtml' does not inherit from 'System.Web.WebPages.WebPage'.]
System.Web.UI.Util.CheckAssignableType(Type baseType, Type type) +9633480
System.Web.Compilation.BuildManager.CreateInstanceFromVirtualPath(VirtualPath virtualPath, Type requiredBaseType, HttpContext context, Boolean allowCrossApp) +66
System.Web.Compilation.BuildManager.CreateInstanceFromVirtualPath(String virtualPath, Type requiredBaseType) +28
System.Web.WebPages.BuildManagerWrapper.CreateInstanceOfType(String virtualPath) +203
System.Web.WebPages.VirtualPathFactoryExtensions.CreateInstance(IVirtualPathFactory factory, String virtualPath) +145
System.Web.WebPages.VirtualPathFactoryManager.CreateInstanceOfType(String virtualPath) +153
System.Web.WebPages.VirtualPathFactoryExtensions.CreateInstance(IVirtualPathFactory factory, String virtualPath) +73
System.Web.WebPages.WebPageHttpHandler.CreateFromVirtualPath(String virtualPath, IVirtualPathFactory virtualPathFactory) +23
System.Web.WebPages.WebPageRoute.DoPostResolveRequestCache(HttpContextBase context) +349
System.Web.WebPages.WebPageHttpModule.OnApplicationPostResolveRequestCache(Object sender, EventArgs e) +89
System.Web.SyncEventExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() +136
System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) +69
Any more ideas? Oh, and creating a new application didn't help, but it was a good idea.
It could be that an older version of System.Web.WebPages.dll is loaded to memory, and it tries to cast the your cshtml page to a version of WebPages class from that dll.
To test this, try to see what http modules are currently registered:
var allModules = HttpContext.Current.ApplicationInstance.Modules;
for( int i = 0; i < allModules.Count; i++ ) {
Trace(allModules.GetKey(i));
}
In my case that was:
....
__DynamicModule_System.Web.WebPages.WebPageHttpModule, System.Web.WebPages, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35_bca8e05a-5746-45b0-be95-2b920b455ccf
__DynamicModule_System.Web.WebPages.WebPageHttpModule, System.Web.WebPages, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35_c1a67b42-31a9-47f1-8483-9e712fabe2a7
To fix the problem you need to replace the older version of System.Web.WebPages.dll in your /Bin folders, or some other dlls that might be referencing it.
You can try explicitly setting the ContentType in the action:
public ActionResult NotFound() {
Response.ContentType = "text/html";
return View(); }

asp.net error: This is an invalid webresource request

I am getting a ton of error alerts from one of my asp servers in a web farm, and all the servers have the same machine key in their web.config files.
I've looked around here but didn't find this exact case answered. Any suggestions would be welcome.
thanks for considering the question!
Here are some of the the error stacks (the query string is not from our app and could be part of something else):
Error message: This is an invalid
webresource request. Stack trace:
at
System.Web.Handlers.AssemblyResourceLoader.System.Web.IHttpHandler.ProcessRequest(HttpContext
context) at
System.Web.HttpApplication.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute()
at
System.Web.HttpApplication.ExecuteStep(IExecutionStep
step, Boolean& completedSynchronously)
Source: System.Web Method: Void
System.Web.IHttpHandler.ProcessRequest(System.Web.HttpContext)
Page: QueryString: d=hAGTq1Iohid
These may have started when the Win 2003 server was patched. Later dot.net frameworks were installed on three other servers but not on the one throwing the errors.
Most of the error seem related to decryption but all servers in the same web farm now same machine key in their web config. But that has not stopped the errors.
Seeing as well :
"Length of the data to decrypt is invalid"
Error message: Length of the data to decrypt is invalid. Stack trace: at System.Security.Cryptography.RijndaelManagedTransform.TransformFinalBlock(Byte[] inputBuffer, Int32 inputOffset, Int32 inputCount) at System.Security.Cryptography.CryptoStream.FlushFinalBlock() at System.Web.Configuration.MachineKeySection.EncryptOrDecryptData(Boolean fEncrypt, Byte[] buf, Byte[] modifier, Int32 start, Int32 length, Boolean useValidationSymAlgo) at System.Web.UI.Page.DecryptString(String s) at
System.Web.Handlers.AssemblyResourceLoader.System.Web.IHttpHandler.ProcessReques­t(HttpContext context) at System.Web.HttpApplication.CallHandlerExecutionStep.System.Web.HttpApplication.I­ExecutionStep.Execute() at System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously)
And Also
"Invalid length for a Base-64 char array"
Error message: Invalid length for a Base-64 char array.
Stack trace: at System.Convert.FromBase64CharArray(Char[] inArray, Int32 offset, Int32 length)
at System.Web.HttpServerUtility.UrlTokenDecode(String input)
at System.Web.UI.Page.DecryptString(String s)
at System.Web.Handlers.AssemblyResourceLoader.System.Web.IHttpHandler.ProcessRequest(HttpContext context)
at System.Web.HttpApplication.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute()
at System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously)
Source: mscorlib
Method: Byte[] FromBase64CharArray(Char[], Int32, Int32)
And
"Invalid view state"
Error message: Invalid viewstate.
Stack trace: at System.Web.UI.Page.DecryptString(String s)
at System.Web.Handlers.AssemblyResourceLoader.System.Web.IHttpHandler.ProcessRequest(HttpContext context)
at System.Web.HttpApplication.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute()
at System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously)
Source: System.Web
Method: System.String DecryptString(System.String)
Thanks again everyone.
I can't be sure, but it looks like a hack attempt trying to exploit the very serious .NET 'Padding Oracle' Crypto Attack, also discussed by Scott Guthrie. There is a patch available, so if you find that your server is indeed vulnerable, make sure you patch it immediately, and take damage-control steps if it has in fact been compromised.
This could also be caused by a problem with your application. However, if you're suddenly seeing large amounts of these errors, especially without any recent changes to your app, my first thought would be an intrusion attempt. The method used to exploit this vulnerability would generate errors related to decryption and bad data length.
If this server is the only one that is both unpatched AND showing the errors then the very first thing I would do is run all the windows updates on it.
Actually, let me make that statement less complicated: If this server is unpatched, start there and apply them.
UPDATE
You mentioned that the servers are throwing viewstate length errors. One thing we found was that several components, notably data grids, shove a tremendous amount of data into viewstate. Another thing was that several browsers would choke on this depending on length and not send all of it back.
After we made a particular web.config change this problem went completely away. Even Safari 3 started working right. Go to the <system.web> <pages /> node and make the following change:
<pages maxPageStateFieldLength="500" />
This will cause viewstate to be broken up across multiple hidden fields at 500 bytes each. It's an automatic split so none of your regular code has to change to support it.
You might play with the value, but essentialy some browsers can only send so much data back per input field. Depending on the version I've seen safari choke when those values were much more than around 700 bytes or so. Also firewalls and proxy servers might cause issues as well. See the following for a bit more information: http://weblogs.asp.net/lduveau/archive/2007/04/17/viewstate-chunking-in-asp-net-2-0-maxpagestatefieldlength.aspx
Another option to pursue is to have viewstate cached locally and not send it across the wire at all. Of course, that has implications in a web farm scenario so pick your poison.
Happened to me just now on a web site, for no apparent reason: The request for a web resource generated "invalid request" error and all the scripts were broken.
Could be an update to the server, maybe..
My solution was to go to the IIS control panel and to generate a new machine key for the site.
This seems to solve the problem, whatever that was.

Is this an attempt to break my ASP.Net site's security?

I am fairly new to ASP.NET. I recently set up automated email from my website to notify me of an unhandled exceptions. Just a few hours ago in 3 minutes there were 10 unhandled exceptions and all stack traces were similar. There is a lot in the error messages I do not understand, but I do not like the way this looks.
Here is one of the email messages:
An unhandled exception occurred:
Message: Padding is invalid and cannot be removed.
Stack Trace:
at System.Security.Cryptography.RijndaelManagedTransform.DecryptData(Byte[] inputBuffer, Int32 inputOffset, Int32 inputCount, Byte[]& outputBuffer, Int32 outputOffset, PaddingMode paddingMode, Boolean fLast)
at System.Security.Cryptography.RijndaelManagedTransform.TransformFinalBlock(Byte[] inputBuffer, Int32 inputOffset, Int32 inputCount)
at System.Security.Cryptography.CryptoStream.FlushFinalBlock()
at System.Web.Configuration.MachineKeySection.EncryptOrDecryptData(Boolean fEncrypt, Byte[] buf, Byte[] modifier, Int32 start, Int32 length, IVType ivType, Boolean useValidationSymAlgo)
at System.Web.UI.Page.DecryptStringWithIV(String s, IVType ivType)
at System.Web.Handlers.AssemblyResourceLoader.System.Web.IHttpHandler.ProcessRequest(HttpContext context)
at System.Web.HttpApplication.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute()
at System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously)
Is this an attempt to hack into my site or something else?
Many thanks go to those who provided Answers and Comments provided which pointed me in the right direction to get an answer to this exception. It's cause can be difficult to determine, especially when not occuring in a web farm.
IE 5.5 was not causing the problem as I had earlier thought.
It was not easily found, but a post on my web host forum by one of the staff mentioned reports of Viewstate errors. The cause was attributed to the asp worker process or the server recycling.
The asp worker process settings refers to the processModel Element in the machine.config file. See http://msdn.microsoft.com/en-us/library/7w2sway1(VS.80).aspx for more information.
The recommended fix was to set an encrypted machineKey in the web.config file. The Machine Key node is in the system.web element.
This was easily done and solved the problem thanks to the handy ASP.NETResources site which has a MachineKey Generator. See http://www.aspnetresources.com/tools/keycreator.aspx.
This exception is thrown when the assembly resource handler gets an invalid request. It is unlikely this is related to any malicious activity; it's usually caused by an incorrectly configured machine config on the server.
Some background:
ASP.NET has built-in mechanisms for exposing resources from the assembly (dll) via an HttpHandler. Resources such as JavaScripts and images can be stored as text in the assembly and requested by the browser via .axd handlers. But for security reasons, the handlers don't accept a plain-text location of the resource, which might expose clues about how your code works. Instead, it uses information in the machine.config on the server to encrypt a unique identifier to the resource. This exception gets thrown when a resource is requested, but when the server tries to decrypt the identifier provided, it fails.
The error is because your appdomain was recycled/restarted. When that happens the application and the machine key is set to auto, it changes. That affects the decryption of the info in the url of the resources urls (.axd). Setting up a fixed machine key will prevent it from ever happening again.
Please check this for more info on a similar case (the explanation is with an issue with viewstate validation, but the cause is the same one):
http://www.developmentnow.com/blog/InvalidViewstate+Or+Unable+To+Validate+Data+Error.aspx
Ps. this explains it on single server deployments :) - although the solution is the same for both multi-single server, the fix was usually only explained to make all servers use the same machine key.
Update 1: The padding is invalid message doesn't have relation to the css padding. If it is only happening on ie 5.5, it is likely the parameters for the webresource.axd are being messed up, just like in this question: Invalid Webresource.axd parameters being generated.

ASP.NET URL validation

We have a custom REST handler on ASP.NET that is configured like this to handle all incoming requests:
<add path="*" verb="*" type="REST.RESTProtocolHandler"/>
However, passing it a pipe character, properly encoded or not at all, triggers a validation error that seems to come from inside ASP.NET.
Accessing http://localhost:8080/%7c or http://localhost:8080/| yields this error:
[ArgumentException: Illegal characters in path.]
System.IO.Path.CheckInvalidPathChars(String path) +7489125
System.IO.Path.Combine(String path1, String path2) +40
System.Web.Configuration.UserMapPath.GetPhysicalPathForPath(String path, VirtualDirectoryMapping mapping) +114
System.Web.Configuration.UserMapPath.GetPathConfigFilename(String siteID, VirtualPath path, String& directory, String& baseName) +72
System.Web.Configuration.UserMapPath.MapPath(String siteID, VirtualPath path) +30
System.Web.Configuration.UserMapPath.MapPath(String siteID, String path) +31
System.Web.Hosting.HostingEnvironment.MapPathActual(VirtualPath virtualPath, Boolean permitNull) +297
System.Web.Hosting.HostingEnvironment.MapPathInternal(VirtualPath virtualPath, Boolean permitNull) +51
System.Web.CachedPathData.GetConfigPathData(String configPath) +341
System.Web.CachedPathData.GetVirtualPathData(VirtualPath virtualPath, Boolean permitPathsOutsideApp) +110
System.Web.HttpContext.GetFilePathData() +36
System.Web.HttpContext.GetConfigurationPathData() +26
System.Web.Configuration.RuntimeConfig.GetConfig(HttpContext context) +43
System.Web.Configuration.CustomErrorsSection.GetSettings(HttpContext context, Boolean canThrow) +41
System.Web.HttpResponse.ReportRuntimeError(Exception e, Boolean canThrow, Boolean localExecute) +101
System.Web.HttpRuntime.FinishRequest(HttpWorkerRequest wr, HttpContext context, Exception e) +383
No userland code gets executed. Is this a configuration option somewhere? Reproduced on IIS 7 & VS Studio's 2008 devel server.
Stack Overflow seems to handle this error OK, it looks like a dynamically generated 404 MVC page gets rendered for https://stackoverflow.com/%7c.
Any ideas?
Try to intercept the exception in Global.asax file. Implement there (Global.asax.cs) this method:
protected void Application_Error(Object sender, EventArgs e)
{
Exception ex = Server.GetLastError();
//do whatever you want with that exception
//or get the url from the context, reformat and redirect
}
First you need to tinker in the
Registry :
http://support.microsoft.com/default.aspx?scid=kb;EN-US;826437
Restart IIS
and voila it works.
But i've made this work with IIS7 without problems, but with IIS6 I get this error (Illegal characters in path).
I have a similar program that intercepts all and trying it with a pipe gives me the same error. I assume it has to do with IIS doing path tests (mappath) before it knows who is to handle the request.
Your handler takes the root(meaning all calls) but i assume the way IIS does it is generic.
So I assume any or most of pathing characters that you cant use on your filesystem will fail on IIS request (GET/POST).
Maybe someone knows how to disable the IIS check. According to the error, it seems to happens even before your web.config is read, as it is trying to locate the right config?,
Maybe it is possible to use your own error page as an redirect back to your handler?
I think the answer is in your stack trace. The error is thrown on the System.IO.Path.CheckInvalidPathChars() call - this is not checking the Url, but checking the Windows file system upon which IIS sits. It's not so much a case of the pipe character being Url illegal, but basically DOS illegal.
If you intercept the Url before IIS tries to find the matching path on the server, I expect you can deal with this error. That probably lies in having a rewrite rule or similar to find and rewrite the Url with unwanted characters in it.
By default IIS does not allow certain characters in the URL and considers them illegal. This is where you problem comes from - it doesn't even call the handler you have. As far as I know there is no place that you can configure which characters are accepted through UI, except for Windows Registry. I don't know why you want to use pipe, but I don't think it is good practice. As for the error page - you can always have your own error page for any exception, so that users don't see the ugly messages.

Resources