Web.config in a subdirectory ignored - asp.net

We have a web.config in a physical subdirectory of a virtual directory that's under an application in an IIS site. Something like this:
Site
App
Web.config
Virtual Dir
Subdir
Web.config
In the Web.config we put this configuration in system.web:
<webServices>
<protocols>
<add name="HttpPost" />
<add name="HttpGet" />
</protocols>
</webServices>
We enable both protocols for an ASMX in that subdirectory.
It all works fine for a while and after that it just stops and those protocols just stop working. We restart IIS and it starts working again.
To fix this, we have used a workaround to add that configuration to the application Web.config and then it just works fine. But we would like to avoid changing the application Web.config and instead make the subdirectory Web.config work.
Any ideas why ASP.Net would just stop considering the subdirectory Web.config after a while?
We're hosting on Windows Server 2003, IIS 6, ASP.Net 2.0.
HTTP POST requests to the ASMX stop working. The error we're getting is System.InvalidOperationException with this message:
Request format is unrecognized for URL unexpectedly ending in '/blah'.
The stack trace is:
at System.Web.Services.Protocols.WebServiceHandlerFactory.CoreGetHandler(Type type, HttpContext context, HttpRequest request, HttpResponse response)
at System.Web.Services.Protocols.WebServiceHandlerFactory.GetHandler(HttpContext context, String verb, String url, String filePath)
at System.Web.HttpApplication.MapHttpHandler(HttpContext context, String requestType, VirtualPath path, String pathTranslated, Boolean useAppConfig)
at System.Web.HttpApplication.MapHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute()
at System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously)

It's unlikely that get and post are really being ignored intermittently.
What is your ASMX doing? If you're allocating and not freeing a resource - like a connection to another service or WCF object - or entering a long-running task, IIS can stop responding to requests when those resources are exhausted. That would explain with restarting IIS fixes the problem.
Are you getting no response at all, an error 500, or what? Anything in the event log?

Related

Startup fails after deployment when using connection strings configSource attribute along with Azure App Service environment connection strings

I'm trying to follow Best practices for private config data and connection strings in configuration in ASP.NET and Azure and Best practices for deploying passwords and other sensitive data to ASP.NET and Azure App Service.
Steps I took:
I have an ASP.NET 4.6 Web App with a regular web.config file. I created two files for my secrets: Web.Secrets.AppSettings.config and Web.Secrets.ConnectionString.config, put corresponding secrets into them according to the tutorial and modified the root web.config so it looks like that:
<configuration>
<appSettings file="Web.Secrets.AppSettings.config">
</appSettings>
<connectionStrings configSource="Web.Secrets.ConnectionStrings.config">
</connectionStrings>
<!--...-->
</configuration>
Then I created a new Azure App Service in Azure Portal, opened it's Application Settings and added the secrets into the corresponding sections (App Settings and Connection Strings).
After that I deployed my Web App to this Azure App Service and right after that at the startup got yellow screen of death with the following message:
Server Error in '/' Application.
Configuration Error
Description: An error occurred during the processing of a
configuration file required to service this request. Please review the
specific error details below and modify your configuration file
appropriately.
Parser Error Message: Unable to open configSource file
'Web.Secrets.ConnectionStrings.config'.
Source Error:
An application error occurred on the server. The current custom error
settings for this application prevent the details of the application
error from being viewed remotely (for security reasons). It could,
however, be viewed by browsers running on the local server machine.
Source File: D:\home\site\wwwroot\web.config Line: 8
Version Information: Microsoft .NET Framework Version:4.0.30319;
ASP.NET Version:4.6.1590.0
Of course Web.Secrets.ConnectionStrings.config and Web.Secrets.AppSettings.config are not copied and it's exactly what I need. The corresponding secrets should be taken from the environment variables.
There is a stack trace in HTML Source of the error page:
[ConfigurationErrorsException]: Unable to open configSource file 'Web.Secrets.ConnectionStrings.config'. (D:\home\site\wwwroot\web.config line 8)
at System.Configuration.BaseConfigurationRecord.EvaluateOne(String[] keys, SectionInput input, Boolean isTrusted, FactoryRecord factoryRecord, SectionRecord sectionRecord, Object parentResult)
at System.Configuration.BaseConfigurationRecord.Evaluate(FactoryRecord factoryRecord, SectionRecord sectionRecord, Object parentResult, Boolean getLkg, Boolean getRuntimeObject, Object& result, Object& resultRuntimeObject)
at System.Configuration.BaseConfigurationRecord.GetSectionRecursive(String configKey, Boolean getLkg, Boolean checkPermission, Boolean getRuntimeObject, Boolean requestIsHere, Object& result, Object& resultRuntimeObject)
at System.Configuration.BaseConfigurationRecord.GetSection(String configKey)
at System.Web.Configuration.HttpConfigurationSystem.GetApplicationSection(String sectionName)
at System.Web.Configuration.HttpConfigurationSystem.GetSection(String sectionName)
at System.Web.Configuration.HttpConfigurationSystem.System.Configuration.Internal.IInternalConfigSystem.GetSection(String configKey)
at System.Configuration.ConfigurationManager.GetSection(String sectionName)
at System.Configuration.ConfigurationManager.get_ConnectionStrings()
at EnvSettings.SettingsProcessor.SetConnectionString(String name, String connString, String providerName)
at EnvSettings.SettingsProcessor.Start()
[InvalidOperationException]: The pre-application start initialization method Start on type EnvSettings.SettingsProcessor threw an exception with the following error message: Unable to open configSource file 'Web.Secrets.ConnectionStrings.config'. (D:\home\site\wwwroot\web.config line 8).
at System.Web.Compilation.BuildManager.InvokePreStartInitMethodsCore(ICollection`1 methods, Func`1 setHostingEnvironmentCultures)
at System.Web.Compilation.BuildManager.InvokePreStartInitMethods(ICollection`1 methods)
at System.Web.Compilation.BuildManager.CallPreStartInitMethods(String preStartInitListPath, Boolean& isRefAssemblyLoaded)
at System.Web.Compilation.BuildManager.ExecutePreAppStart()
at System.Web.Hosting.HostingEnvironment.Initialize(ApplicationManager appManager, IApplicationHost appHost, IConfigMapPathFactory configMapPathFactory, HostingEnvironmentParameters hostingParameters, PolicyLevel policyLevel, Exception appDomainCreationException)
[HttpException]: The pre-application start initialization method Start on type EnvSettings.SettingsProc
What am I doing wrong? Or it's just a bug in Azure?
Important notes
The app fails right on the startup. I don't even touch anything related to config files anywhere.
If I delete the connection string from connection strings section in Application Settings of Azure App Service, the app starts fine. If I get it back, the app starts failing at the startup again. This is very odd! Just think about it! There IS connection string - fail, there is NO connection string - fine.
When I run the app locally and there is no Web.Secrets.ConnectionStrings.config file, the app runs just fine. The app fails only when being deployed at Azure App Service. Hence, the issue is Azure App Service specific.
It is being reproduced even with a plain empty ASP.NET Web Application project.
The part about appSettings works just fine, only the part with connectionStrings fails.
Default Azure App Service Environment Variables:
WEBSITE_NODE_DEFAULT_VERSION: 4.4.7
Possible workaround:
Config transformation could be used for removing the appropriate attribute in Web.config. For example, that's how Web.Release.config might look like:
<?xml version="1.0" encoding="utf-8"?>
<configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
<connectionStrings xdt:Transform="RemoveAttributes(configSource)"/>
<system.web>
<compilation xdt:Transform="RemoveAttributes(debug)" />
</system.web>
</configuration>
I looked into this, and my conclusion is that this is not an Azure issue, but is the way the config system behaves with a connection string configSource. Specifically, the behavior is that when you specify such directive, that file must be present, or any attempt to access connection strings blows up. e.g. outside of Azure, set up your web.config pointing to a missing configSource and run:
ConnectionStringSettings settings = ConfigurationManager.ConnectionStrings["foo"];
And it will blow up in the same way (Unable to open configSource file 'Web.Secrets.ConnectionStrings.config'.).
It's interesting, because with App Settings, it's able to simply ignore a file directive when the file is missing.
But none of that is Azure specific. It's just .NET framework config system behavior. I create a trivial Console app which demonstrates that: https://github.com/davidebbo-test/ConsoleAppWithMissingConfigSourceFile
You'll need to either yank the attribute (as you're showing), or deploy a dummy file to keep it happy.

How to restrict access to static files in IIS 7.5 Classic mode

I need to restrict access to static HTML files in several ASP.NET applications. As those applications implement their own authentication mechanisms it looks like I need just to get those files to go through the ASP.NET request processing pipeline instead of the "standard" IIS static content handling. Some of applications are relatively modern ASP.NET MVC applications and use integrated pipeline on IIS 7.5 and it looks like it is not a big deal to do what I need for them. But others are legacy ASP.NET web sites working on the classic pipeline mode (IIS 7.5) and at the moment it is not possible to move them to the integrated pipeline for certain reasons.
UPD: The issue is that applications contain some static files (help pages) and unauthorized user can see them if they type the direct URL. And my task is to make the application show the login page in such a case.
Adding a handler into Web.config:
<add name="StaticHTMLHandler" path="*.htm*" verb="*" modules="IsapiModule" scriptProcessor="C:\Windows\Microsoft.NET\Framework\v4.0.30319\aspnet_isapi.dll" resourceType="Unspecified" preCondition="classicMode,runtimeVersionv4.0,bitness32" />
caused me just to get this exception:
[HttpException (0x80004005): Failed to Execute URL]
System.Web.Hosting.ISAPIWorkerRequestInProcForIIS6.BeginExecuteUrl(String url, String method, String childHeaders, Boolean sendHeaders, Boolean addUserIndo, IntPtr token, String name, String authType, Byte[] entity, AsyncCallback cb, Object state) +4136452
System.Web.HttpResponse.BeginExecuteUrlForEntireResponse(String pathOverride, NameValueCollection requestHeaders, AsyncCallback cb, Object state) +653
System.Web.DefaultHttpHandler.BeginProcessRequest(HttpContext context, AsyncCallback callback, Object state) +279
System.Web.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() +12551795
System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) +288
So what am I doing wrong? Can anybody point me to the right way?
I've never tried your methods, but I also have some FW 1.1, classic asp. and modern web apps mixed on my web servers. What I do is create a seperate app pool per framework. I run 1.1 on 1.1 classic mode app pool that I create, and run modern apps on another app pool: integrated fw 4.0. The classic asp stuff is out of scope for this question. Each app pool is it's own process, so there's no real downside to using more app pools, 1 app pool per application is a perfectly acceptable security policy for example.
So finally no practical way was found for bringing authentication to static files with the classic pipeline mode. Finally we have decided to update the old legacy platform and make it work with the integrated pipeline mode.

ASP.NET 4.0 application cannot find Default.aspx under IIS6

I've got an ASP.NET 4.0 web application (webforms, not mvc; asp.net routing isn't used) that runs fine under IIS7. When I try to run it under IIS6 and navigate to http://localhost/MyApp/, I get the following exception:
File does not exist.
System.Web.HttpException
at System.Web.StaticFileHandler.GetFileInfo(String virtualPathWithPathInfo, String physicalPath, HttpResponse response) (+0 IL, +2509040 JIT)
at System.Web.StaticFileHandler.ProcessRequestInternal(HttpContext context, String overrideVirtualPath) (+54 IL, +198 JIT)
at System.Web.DefaultHttpHandler.BeginProcessRequest(HttpContext context, AsyncCallback callback, Object state) (+263 IL, +347 JIT)
at System.Web.HttpApplication.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() (+214 IL, +8967220 JIT)
at System.Web.HttpApplication.ExecuteStep(HttpApplication.IExecutionStep step, Boolean& completedSynchronously) (+54 IL, +184 JIT)
Note that this output comes from my own custom error page. Thus, .NET itself is working fine. I can even remotely debug it and stuff.
Now, if I enter http://localhost/MyApp/Default.aspx, all works fine, I get the default page, etcetera. The first thought would be that the default document isn't specified in IIS, but it is. Even worse - if I disable it altogether, I still get the same error message (and yes, I restarted IIS and cleared my browser cache)!
It seems as if the request for / is always sent directly to ASP.NET which then gets confused because it doesn't have any default document concept. But I don't have any wildcard mappings defined, so how can that be?
After Googling I found this one as a solution. Its unrelated but some commented its working.
Can you please try this.
In the Windows registry, open the following node: HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\ASP.NET\4.0.30319.0
1.Create a new DWORD value named EnableExtensionlessUrls.
2.Set EnableExtensionlessUrls to 0. This disables extensionless URL behavior.
3.Save the registry value and close the registry editor.
4.Run the iisreset command-line tool, which causes IIS to read the new registry value

Handle URI hacking gracefully in 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.

WebRequest from localhost to localhost : why is it being denied?

My app uses a WebRequest at certain points to get pages from itself.
This shouldn't be a problem. It actually works fine on the server, which is a "shared" hosting package with Medium trust. Locally, I use a custom security policy based on Medium trust, which includes the following — copied straight from the default Medium trust policy:
<IPermission
class="WebPermission"
version="1">
<ConnectAccess>
<URI uri="$OriginHost$"/>
</ConnectAccess>
</IPermission>
The offending line is in a custom XmlRelativeUrlResolver:
public override object GetEntity( System.Uri puriAbsolute, string psRole, System.Type pReturnType )
{
return _baseResolver.GetEntity( puriAbsolute, psRole, pReturnType );
}
The url being requested is on localhost, in the same application as the requester. Here's the top of the stack trace.
at System.Security.CodeAccessSecurityEngine.Check(Object demand, StackCrawlMark& stackMark, Boolean isPermSet)
at System.Security.CodeAccessPermission.Demand()
at System.Net.HttpWebRequest..ctor(Uri uri, ServicePoint servicePoint)
at System.Net.HttpRequestCreator.Create(Uri Uri)
at System.Net.WebRequest.Create(Uri requestUri, Boolean useUriBase)
at System.Net.WebRequest.Create(Uri requestUri)
at System.Xml.XmlDownloadManager.GetNonFileStream(Uri uri, ICredentials credentials)
at System.Xml.XmlDownloadManager.GetStream(Uri uri, ICredentials credentials)
at System.Xml.XmlUrlResolver.GetEntity(Uri absoluteUri, String role, Type ofObjectToReturn)
at flow.controls.XmlRelativeUrlResolver.GetEntity(Uri puriAbsolute, String psRole, Type pReturnType) in c:\flow\source\controls\DataTransform.cs:line 105
at System.Xml.Xsl.Xslt.XsltLoader.CreateReader(Uri uri, XmlResolver xmlResolver)
Anyone see the problem here?
#Sijin: Thanks for the suggestion. The url that gets sent to the resolver is based on the request URL, and I confirmed in the debugger that accessing the site at 127.0.0.1 yields the same result.
Does it work if you put 127.0.0.1 instead of localhost?
My ignorance. I didn't know that the $OriginHost$ token was replaced using the originUrl attribute of the trust level — I thought it just came from the url of the app. I had originally left this attribute blank.
<trust level="CustomMedium" originUrl="http://localhost/" />
This might not be the solution but when I saw your post I remembered this issue that I ran into about a year ago:
http://support.microsoft.com/default.aspx/kb/896861
You receive error 401.1 when you
browse a Web site that uses Integrated
Authentication and is hosted on IIS
5.1 or IIS 6
We were creating a WebRequest to screen scrape a page and it worked in our production environment because we were not using a loopback host name but on development machines we ended up with access denied (after applying Windows Server 2003 SP2). The one difference here is that this was under integrated authentication which caused it to fail... it worked when the request was anonymous (so that is why I am not sure this is the answer for you).

Resources