I have an ASP.NET 4.0 application running on Windows 7 / IIS 7.5 in the "ASP.NET v4.0 Classic" application pool, which is configured to run as Network Service. The application has an Application_EndRequest
handler which connects to a local SQL Server instance. The SQL connection string specifies Integrated Security=SSPI
. Web.config does not have <identity impersonate="true" />
.
When I browse to http://localhost/TestSite/
, the following exception is thrown:
System.Data.SqlClient.SqlException (0x80131904): Login failed for user 'NT AUTHORITY\IUSR'.
...
at System.Data.SqlClient.SqlConnection.Open()
at Global.Application_EndRequest(Object sender, EventArgs e)
This exception is not thrown when I browse to http://localhost/TestSite/default.aspx
(the default document configured in IIS) or any other .aspx page; in those cases the application correctly connects to SQL Server as "NT AUTHORITY\NETWORK SERVICE", which is a valid login.
Why would ASP.NET impersonate "NT AUTHORITY\IUSR" in EndRequest even though impersonation is disabled? Is this a bug in ASP.NET?
The following Global.asax.cs file demonstrates the problem:
public class Global : HttpApplication
{
public Global()
{
this.BeginRequest += delegate { Log("BeginRequest"); };
this.PreRequestHandlerExecute += delegate { Log("PreRequestHandlerExecute"); };
this.PostRequestHandlerExecute += delegate { Log("PostRequestHandlerExecute"); };
this.EndRequest += delegate { Log("EndRequest"); };
}
protected void Application_EndRequest(Object sender, EventArgs e)
{
try
{
using (SqlConnection connection = new SqlConnection("Server=.;Integrated Security=SSPI"))
{
connection.Open();
}
}
catch (Exception ex)
{
Trace.WriteLine(ex);
}
}
private static void Log(string eventName)
{
HttpContext context = HttpContext.Current;
Type impersonationContextType = typeof(HttpContext).Assembly.GetType("System.Web.ImpersonationContext", true);
Trace.WriteLine(string.Format("ThreadId={0} {1} {2} Impersonating={3}",
Thread.CurrentThread.ManagedThreadId,
context.Request.Url,
eventName,
impersonationContextType.InvokeMember("CurrentThreadTokenExists", BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.GetProperty, null, context, null)));
}
}
Here's the trace output:
ThreadId=3 http://localhost/TestSite/ BeginRequest Impersonating=False
ThreadId=3 http://localhost/TestSite/ PreRequestHandlerExecute Impersonating=False
ThreadId=7 http://localhost/TestSite/default.aspx BeginRequest Impersonating=False
ThreadId=7 http://localhost/TestSite/default.aspx PreRequestHandlerExecute Impersonating=False
ThreadId=7 http://localhost/TestSite/default.aspx PostRequestHandlerExecute Impersonating=False
ThreadId=7 http://localhost/TestSite/default.aspx EndRequest Impersonating=False
ThreadId=7 http://localhost/TestSite/ PostRequestHandlerExecute Impersonating=True
ThreadId=7 http://localhost/TestSite/ EndRequest Impersonating=True
System.Data.SqlClient.SqlException (0x80131904): Login failed for user 'NT AUTHORITY\IUSR'.
...
at System.Data.SqlClient.SqlConnection.Open()
at Global.Application_EndRequest(Object sender, EventArgs e)
Note that a request to TestSite/
(which is mapped to DefaultHttpHandler
) seems to spawn a nested request to TestSite/default.aspx
(which is mapped to ASP.default_aspx
). After ASP.NET finishes processing TestSite/default.aspx
, it impersonates "NT AUTHORITY\IUSR" when it resumes processing the request to TestSite/
.
UPDATE: I've submitted this issue to Microsoft Connect.
Most likely, the settings for your server, site or application are set so that "Anonymous Authentication" mode causes page requests to be handled as the IUSR user. It doesn't matter that your application is not requesting impersonation; IIS is forcing it. (By the way, "impersonation" is generic term in Windows-land for assuming another user's credentials. It is not specific to ASP.NET.)
A bit of background:
For security reasons, IIS allows your server to field "anonymous" and "authenticated" requests under different system credentials.
Now, in IIS 7.5, if you have both anonymous authentication and Forms authentication enabled (which is typical), before your website user logs in via Forms, it considers your user "anonymous". After your user logs in with Forms Auth, it considers your user "authenticated."
I found this behavior confusing at first because it's a change from IIS 6.0, which wasn't aware of Forms auth, and considered all Forms-Authenticated users to be anonymous!
If you want, you can change the identity under which anonymous requests are fielded. My preference (and it sounds like yours too) is that they run under the same system credentials as my site's app pool. To make IIS do that, do the following:
Step 5 also confused me at first, because I thought "application pool identity" meant "the application pool pseudo-account" (another new feature in IIS 7.5). What it really means, of course, is "the same account the app pool is configured to run under, whatever that is."
If you want this behavior by default for all of your websites on that server, or even just single application under a particular site, you can configure the authentication options at those levels too. Just select the node you want to configure in the Connections pane, then repeat steps 3-6.