Final period in IIS URL causes 404 - asp.net

URIs with a trailing period seems to be problematic for IIS. I have configured IIS to include a wide range of characters for my ASP.NET MVC 5 app, including periods and other characters (&, %, +, - etc.). Only in cases of period as the last character does IIS give a 404 without ever hitting the controller.
I've added some commonly suggested points of configuration to allow the unusual characters. I have a large set of data with little control over the URIs permissible. The data is all served by the controller and does not map to a file system.
<location path="data">
<system.web>
<httpRuntime targetFramework="4.5.2" requestPathInvalidCharacters="" relaxedUrlToFileSystemMapping="true"/>
<httpHandlers>
<add path="*" type="System.Web.Handlers.TransferRequestHandler" verb="GET,HEAD,POST,DEBUG,PUT,DELETE,PATCH,OPTIONS" />
</httpHandlers>
</system.web>
<system.webServer>
<security>
<requestFiltering allowDoubleEscaping="true" />
</security>
<modules runAllManagedModulesForAllRequests="true" />
<validation validateIntegratedModeConfiguration="false" />
<handlers>
<add name="DataHandler" path="*" verb="GET" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0" />
</handlers>
</system.webServer>
</location>
What am I missing?

Related

Allow colon (:) in URL for ASP.NET Core in IIS/Azure

I've got an ASP.NET Core app that I'm deploying to Azure that takes in a string in the URL that contains colon (a time stamp).
For example: http://localhost:5000/Servers/208.100.45.135/28000/2017-03-15T07:03:43+00:00, or http://localhost:5000/Servers/208.100.45.135/28000/2017-03-15T07%3a03%3a43%2B00%3a00 URL-encoded.
This works perfectly fine when running locally using Kestrel (dotnet run), but after deploying to Azure I receive this error: The resource you are looking for has been removed, had its name changed, or is temporarily unavailable.
A quick search reveals that it's due to invalid characters being used in the URL, namely the colon. The traditional fix is to add this section to web.config:
<system.web>
<httpRuntime requestPathInvalidCharacters="" />
</system.web>
However, after adding this to my web.config on Azure, I observe no change. I imagine this is due to differences in ASP.NET Core's hosting model.
Here is my current web.config:
<configuration>
<system.web>
<httpRuntime requestPathInvalidCharacters=""/>
<pages validateRequest="false" />
</system.web>
<system.webServer>
<handlers>
<add name="aspNetCore" path="*" verb="*" modules="AspNetCoreModule" resourceType="Unspecified" />
</handlers>
<aspNetCore processPath="dotnet" arguments=".\Server.dll" stdoutLogEnabled="false" stdoutLogFile=".\logs\stdout" forwardWindowsAuthToken="false" />
</system.webServer>
</configuration>
And the relevant controller header...
[HttpGet]
[Route("{serverIpAddress}/{serverPort}/{approxMatchStartTimeStr}")]
public IActionResult GetMatchEvents(string serverIpAddress, string serverPort, DateTimeOffset approxMatchStartTimeStr)
{
...
}
How can I get IIS/Azure to allow the colon character in URLs?
The issue you're running into isn't related to the colon (:) in the path, it's really the plus (+) that IIS doesn't like. It doesn't matter if the plus is encoded as "+" or "%2B". You have two options:
Move the plus/DateTimeOffset from the path to the query string where IIS doesn't mind it.
Configure the IIS request filtering module to "allowDoubleEscaping".
Example web.config:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<system.webServer>
<security>
<requestFiltering allowDoubleEscaping="true" />
</security>
<handlers>
<add name="aspNetCore" path="*" verb="*" modules="AspNetCoreModule" resourceType="Unspecified" />
</handlers>
<aspNetCore processPath="dotnet" arguments=".\Server.dll" stdoutLogEnabled="false" stdoutLogFile=".\logs\stdout" forwardWindowsAuthToken="false" />
</system.webServer>
</configuration>
The system.web section of your current web.config isn't relevant to ASP.NET Core.

Stop EPiServer trying to serve requests to my virtual directory

We have a site built in EPiServer and is running on www.mysite.com
Now we have built a small .NET microsite that isn’t part of the EPiServer project that we would like to run as a IIS Virtual Directory www.mysite.com/microsite
At the moment we are seeing 404 being returned for all of the assets on the microsite so www.mysite.com/microsite/assets/js/myjs.js or www.mysite.com/microsite/assets/img/myimg.jpg
The home page of the microsite is served, but with missing assets.Is there a way I can configure the main EPiServer project to ignore all of the requests to my microsites folder structure.
After a while battling this issue we have now got a repeatable solution.
In the parent application (EPiServer solution) we need to add the following location element in the web.config
<location path="MY-IIS-APPLICATION-NAME">
<system.webServer>
<handlers>
<clear />
<add name="wildcard" path="*" verb="*" type="System.Web.StaticFileHandler" />
</handlers>
</system.webServer>
</location>
Then, in the same web.config we wrap the <system.web> and <system.webserver> sections with this element <location path="." inheritInChildApplications="false">
Finally we need to alter the web.config in our IIS-Application to unload the EPiServer handlers and libraries.
So, in the <system.web> section we added these elements
<httpModules>
<clear />
</httpModules>
<httpHandlers>
<clear />
</httpHandlers>
then within the <system.webserver> we make these changes/removals
<handlers>
<remove name="ExtensionlessUrlHandler-Integrated-4.0" />
</handlers>
<modules>
<remove name="InitializationModule" />
<remove name="FirstBeginRequestModule" />
<remove name="Initializer" />
<remove name="WorkflowRuntime" />
<remove name="UrlRewriteModule" />
<remove name="ShellRoutingModule" />
<remove name="ContainerDisposal" />
<remove name="PropertyInjection" />
<remove name="AttributedInjection" />
</modules>
There is every chance that this isn't the solution, but in the last few days we have rolled this out to 6 different projects and it has had the desired effect each time.

ServiceStack web.config settings ignored when using custom path

Introduction
My ServiceStack service handles route parameters that often contain periods ie:
/people/search/b.j./upton. Initially, asp.net/ServiceStack would throw a "404 - Not Found" exception when it encountered this route. I tried encoding %2E the periods with no luck but eventually resolved the issue after seeing some related questions by setting the relaxedUrlToFileSystemMapping property (info) within my web.config.
Problem
This worked perfectly until today when I had to change my service location from the default path to a custom path by adding <location path="api"> (as described here) to my web.config. Since adding the location node in web.config the relaxedUrlToFileSystemMapping setting is no longer applied and my routes with periods /api/people/search/b.j./upton are breaking once again resulting in '404 - Not Found' exceptions from ServiceStack.
Setup
ServiceStack - v3.9.56
IIS 7.5 / IIS Express (happens on both)
web.config:
<location path="api">
<system.web>
<httpHandlers>
<add path="*" type="ServiceStack.WebHost.Endpoints.ServiceStackHttpHandlerFactory, ServiceStack" verb="*" />
</httpHandlers>
<httpRuntime relaxedUrlToFileSystemMapping="true"/>
...
</location>
Question
Anyone have an idea why the relaxedUrlToFileSystemMapping property is ignored when it is moved from the default path to within my custom <location path="api"> in web.config?
Thanks!
After some tinkering with my web.config I was able to resolve this by moving the relaxedUrlToFileSystemMapping entry into it's own node outside of the <location> node. I'm not sure if this is a recommended approach (multiple <system.web> entries?) or might cause some other conflict(s) but after running a full system test of the service everything is working fine again so I'm going with this for now.
My updated and complete web.config for reference:
<configuration>
<system.web>
<httpRuntime relaxedUrlToFileSystemMapping="true"/>
</system.web>
<location path="api">
<system.web>
<httpHandlers>
<add path="*" type="ServiceStack.WebHost.Endpoints.ServiceStackHttpHandlerFactory, ServiceStack" verb="*" />
</httpHandlers>
</system.web>
<connectionStrings>
<add name="AppDb" connectionString="data source=AppHost\SQLEXPRESS;Initial Catalog=db;User Id=AppUser;password=AppPwd;" providerName="System.Data.SqlClient" />
</connectionStrings>
<system.webServer>
<modules runAllManagedModulesForAllRequests="true" />
<validation validateIntegratedModeConfiguration="false" />
<handlers>
<add path="*" name="ServiceStack.Factory" type="ServiceStack.WebHost.Endpoints.ServiceStackHttpHandlerFactory, ServiceStack" verb="*" preCondition="integratedMode" resourceType="Unspecified" allowPathInfo="true" />
</handlers>
</system.webServer>
</location>
</configuration>
Did you change the root path in the AppHost file
public override void Configure(Container container)
{
SetConfig(new EndpointHostConfig { ServiceStackHandlerFactoryPath = "api" });
}

Unable to map an HttpHandler to a "path/*" wildcard mapping

So I've been trying to map an http module to a sub-path of an MVC3 site. It should be pretty simple as I understand it, but it has not been working. The module is setup like so:
<handlers>
<add name="Nancy" path="api/*" verb="*" type="Nancy.Hosting.Aspnet.NancyHttpRequestHandler" allowPathInfo="true" />
</handlers>
A matching section is also there for iis6 so I can run it under webdev.webserver. However testing both deploying to my local iis7 (under Win7) and with webdev.webserver, only /api actually calls the handler. If I call /api/{anything} it just returns a 404.
I'm sure I'm just "doing it wrong (tm)" but any help would be appreciated.
Note: I've also tried a couple other configurations including using a tag and creating a /api folder and adding a web.config to that folder with a full wildcard.
Simple. Just put the path, no wildcard.
<handlers>
<add name="Nancy" path="api" verb="*" type="Nancy.Hosting.Aspnet.NancyHttpRequestHandler" allowPathInfo="true" />
</handlers>
This will match:
/api/{anything}
The URLRoutingModule-4.0 is a catch all handler listed before your nancy handler. It will thus come into play before your handler is ever hit. You can remove the handlers add yours and add them back in like so:
<handlers>
<remove name="BlockViewHandler" />
<remove name="UrlRoutingModule-4.0" />
<add verb="*" path="robots.txt" name="robots" type="System.Web.StaticFileHandler"/>
... custom handlers here
<add name="Nancy" path="api/*" verb="*" type="Nancy.Hosting.Aspnet.NancyHttpRequestHandler" allowPathInfo="true" />
... now add back UrlRoutingModule and BlockViewHandler
<add path="*" verb="*" name="UrlRoutingModule-4.0" type="System.Web.Routing.UrlRoutingModule, System.Web, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" preCondition="managedHandler" />
<add name="BlockViewHandler" path="*" verb="*" preCondition="integratedMode" type="System.Web.HttpNotFoundHandler" />
</handlers>
You can see the handler order in IIS7 under Handler Mappings select View Ordered List and it will list the order in which it loads the handlers top (first) to bottom (last).
You might need a second Web.config in your /api folder
<?xml version="1.0"?>
<configuration>
<system.web>
<httpHandlers>
<clear />
<add name="Nancy" path="*" verb="*" type="Nancy.Hosting.Aspnet.NancyHttpRequestHandler" allowPathInfo="true" />
</httpHandlers>
</system.web>
</configuration>
Similarly, this is what I usually do for "/static" content on websites. I have not found out how to circumvent the need for the seconds web.config.
EDIT
I had a hard time figuring this out when i had to as well and it seems my memory hasnt served me well. I dont specify a path/* handler anywhere instead I have this:
(only specifying simple wildcards/fully qualified paths to go around UrlRouting)
<location path="." inheritInChildApplications="false">
<system.webServer>
<!--
ml: in .NET 4.0 its now safe to remove from the modules section.
Make sure you have a *. mapping to a ExtensionLessUrl hanlder in IIS
this should improve performance a tad albeit neglectable.
see: http://blogs.msdn.com/b/tmarq/archive/2010/04/01/asp-net-4-0-enables-routing-of-extensionless-urls-without-impacting-static-requests.aspx
-->
<validation validateIntegratedModeConfiguration="false" />
<modules runAllManagedModulesForAllRequests="false" />
<handlers>
<remove name="BlockViewHandler" />
<remove name="UrlRoutingModule-4.0" />
<add verb="*" path="robots.txt" name="robots" type="System.Web.StaticFileHandler"/>
.. Some company handlers i can't list
<add path="*" verb="*" name="UrlRoutingModule-4.0" type="System.Web.Routing.UrlRoutingModule, System.Web, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" preCondition="managedHandler" />
<add name="BlockViewHandler" path="*" verb="*" preCondition="integratedMode" type="System.Web.HttpNotFoundHandler" />
</handlers>
</system.webServer>
</location>
Then in my /Content/web.config file I set the following:
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<system.webServer>
<handlers>
<clear />
<add name="StaticFiles" path="*" verb="*" modules="StaticFileModule" resourceType="Either" requireAccess="None" />
</handlers>
<staticContent>
<clientCache cacheControlMaxAge ="31.00:00:00" cacheControlMode="UseMaxAge" />
</staticContent>
</system.webServer>
</configuration>
My handler list for /Content/ looks like this now:
Which is about as sure as I can be anything in /Content/ will be served through StaticFileModule. The trick here seems to be specifying: inheritInChildApplications="false".
Seems the UrlRoutingModule-4.0 is more trouble than it is worth. Instead I've just told MVC3 to ignore the routes. Not a perfect solution but until I have something that works better I'll have to stick with this in RegisterRoutes:
routes.IgnoreRoute("api/{*route}");

How do I protect static files with ASP.NET form authentication on IIS 7.5?

I have a website running on a IIS 7.5 server with ASP.NET 4.0 on a shared host, but in full trust.
The site is a basic "file browser" that allows the visitors to login and have a list of files available to them displayed, and, obviously, download the files. The static files (mostly pdf files) are located in a sub folder on the site called data, e.g. http://example.com/data/...
The site uses ASP.NET form authentication.
My question is: How do I get the ASP.NET engine to handle the requests for the static files in the data folder, so that request for files are authenticated by ASP.NET, and users are not able to deep link to a file and grab files they are not allowed to have?
If you application pool is running in Integrated mode then you can do the following.
Add the following to your top level web.config.
<system.webServer>
<modules>
<add name="FormsAuthenticationModule" type="System.Web.Security.FormsAuthenticationModule" />
<remove name="UrlAuthorization" />
<add name="UrlAuthorization" type="System.Web.Security.UrlAuthorizationModule" />
<remove name="DefaultAuthentication" />
<add name="DefaultAuthentication" type="System.Web.Security.DefaultAuthenticationModule" />
</modules>
</system.webServer>
Now you can use the standard ASP.NET permissions in your web.config to force forms authentication for all files in the directory.
<system.web>
<authorization>
<deny users="?" />
</authorization>
<authentication mode="Forms" />
</system.web>
I had the same problem with getting roles to authenticate. Through trial and error I finally got it to work with a small edit to #Joel Cunningham's code:
<modules runAllManagedModulesForAllRequests="true" >
I used these two sites as references: http://forums.iis.net/t/1177964.aspx and http://learn.iis.net/page.aspx/244/how-to-take-advantage-of-the-iis-integrated-pipeline/
This is an old thread, but I happened on it and ran into the same problem as Egil. Here is the version of Joel's fix that includes roles:
<modules runAllManagedModulesForAllRequests="false">
<remove name="FormsAuthenticationModule" />
<add name="FormsAuthenticationModule" type="System.Web.Security.FormsAuthenticationModule" />
<remove name="UrlAuthorization" />
<add name="UrlAuthorization" type="System.Web.Security.UrlAuthorizationModule" />
<remove name="RoleManager" />
<add name="RoleManager" type="System.Web.Security.RoleManagerModule" />
<remove name="DefaultAuthentication" />
<add name="DefaultAuthentication" type="System.Web.Security.DefaultAuthenticationModule" />
</modules>
Addendum:
As #eych noted the accepted answer also blocks access to the ~/Content folder (or wherever you have your CSS), and ~/Scripts, and so on.
If you want to allow exceptions -- i.e. allow certain files/folders to be accessible by unauthenticated users -- you can do that by means of the location element. Add the following to web.config:
<location path="Content">
<system.web>
<authorization>
<allow users="*" />
</authorization>
</system.web>
</location>
Update:
An alternative solution is to is to leave access on by default -- which will allow access to your CSS / JavaScript / etc. -- and apply the "lock" (only) to the folder where the static content is stored:
<location path="data">
<system.web>
<authorization>
<deny users="?"/>
</authorization>
</system.web>
</location>
Caveat: in our case (an MVC site) we needed to decorate all our controller actions (except login) with [AuthorizeAttribute]. Which is a good idea anyway, but had previously not been necessary (because previously any unauthorized request was redirected to the login page).
I wanted to know why it would be required to re-add modules (with default options) that are added by default for the Integrated Pipeline, so I dug a little deeper.
You need to remove and re-add the modules because, by default, the modules aren't added with the default options. They have a precondition added for backwards compatibility to run only for content handled by a registered ASP.NET handler (e.g., .aspx pages).
The default looks like this:
<add name="FormsAuthentication" type="System.Web.Security.FormsAuthenticationModule"
preCondition="managedHandler" />
By removing the modules and re-adding them without a precondition, those individual modules run for every request (including your static content). It is more granular than enabling runAllManagedModulesForAllRequests.
You can read about it in a couple articles from when the Integrated Pipeline was introduced with IIS 7:
ASP.NET Integration with IIS 7
How to Take Advantage of the IIS 7.0 Integrated Pipeline
Note that there is a typo or the module name in the second article (and #John's answer) was changed from FormsAuthenticationModule to FormsAuthentication at some point.
The set of working modules in IIS 7.5 thru 8.5 looks like this for me:
<system.webServer>
<modules>
<!-- Re-add auth modules (in their original order) to run for all static and dynamic requests -->
<remove name="FormsAuthentication" />
<add name="FormsAuthentication" type="System.Web.Security.FormsAuthenticationModule" />
<remove name="DefaultAuthentication" />
<add name="DefaultAuthentication" type="System.Web.Security.DefaultAuthenticationModule" />
<remove name="RoleManager" />
<add name="RoleManager" type="System.Web.Security.RoleManagerModule" />
<remove name="UrlAuthorization" />
<add name="UrlAuthorization" type="System.Web.Security.UrlAuthorizationModule" />
</modules>
</system.webServer>
If you application pool is running in Classic mode, you can do the following. You will have to repeat these steps for each file extension you'd like to handle, but I'm using .html here.
First, add a page build provider to the Web.config:
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<system.web>
<compilation>
<buildProviders>
<add type="System.Web.Compilation.PageBuildProvider" extension=".html"/>
</buildProviders>
</compilation>
</system.web>
</configuration>
Then add a page handler factory:
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<system.web>
<httpHandlers>
<add type="System.Web.UI.PageHandlerFactory" path="*.html" verb="*"/>
</httpHandlers>
</system.web>
</configuration>
Then add a page handler:
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<system.webServer>
<handlers>
<add scriptProcessor="C:\Windows\Microsoft.NET\Framework\v4.0.30319\aspnet_isapi.dll" requireAccess="Script" preCondition="classicMode,runtimeVersionv2.0,bitness32" path="*.html" verb="GET,HEAD,POST,DEBUG" modules="IsapiModule" name="HtmlHandler-Classic-32" />
<add scriptProcessor="C:\Windows\Microsoft.NET\Framework64\v4.0.30319\aspnet_isapi.dll" requireAccess="Script" preCondition="classicMode,runtimeVersionv2.0,bitness64" path="*.html" verb="GET,HEAD,POST,DEBUG" name="HtmlHandler-Classic-64"/>
</handlers>
</system.webServer>
</configuration>
This worked for me. (Credit: http://www.ifinity.com.au/Blog/EntryId/66/How-To-301-Redirect-htm-or-html-pages-to-DotNetNuke-aspx-pages.)

Resources