patterns & practices Developer Center
J.D. Meier, Alex Mackman, Blaine Wastell, Prashant Bansode, Andy Wigley, Kishore Gopalan
Microsoft Corporation
August 2005
This How To shows you how and when you should use impersonation in ASP.NET 2.0 applications. By default, impersonation is turned off, and you can access resources by using the ASP.NET Web application's process identity. However, you can use impersonation to access local resources and perform operations by using the authenticated user's identity or by using a specific Windows identity. You can enable impersonation programmatically or by applying appropriate configuration settings in the Web.config file.
Delegation allows you to use an impersonation token to access network resources. Your ability to use delegation depends on your selected authentication mechanism and appropriate account configuration. You should be careful when you use impersonation and delegation because of the additional security and scalability issues it can cause.
Objectives
Overview
What's New in 2.0
Impersonation Scenarios
Impersonating the Original Caller
Impersonating the Original Caller Temporarily
Impersonating by Using LogonUser
Impersonating by Using the WindowsIdentity Constructor
Impersonating a Fixed Identity
Delegation
Impersonation / Delegation vs. Trusted Subsystem
Delegation Table
Windows 2000 Server Considerations
Windows Server 2003 Considerations
Additional Resources
By default, ASP.NET does not use impersonation, and your code runs using the ASP.NET application's process identity. On the Microsoft® Windows Server™ 2003 operating system, ASP.NET applications run in an Internet Information Services (IIS) 6.0 application pool by default. The IIS application pool runs under the NT AUTHORITY\Network Service identity. On the Microsoft Windows® 2000 Server operating system with IIS 5.0 or on Windows Server 2003 with IIS 6.0 configured for IIS 5.0 isolation mode, ASP.NET applications run in a worker process that uses the local ASPNET account identity.
Because impersonation is disabled, the application gains access to all resources by using the process identity. Any Windows resources, such as files and registry keys, must have an access control list (ACL) that grants access to the process identity.
If you need to access resources by using the authenticated caller's identity or by using a specific Windows identity other than the process identity, you can configure your ASP.NET application to use impersonation. If you need to impersonate at the method level to perform specific operations or access particular resources, then you can use programmatic impersonation by using the WindowsIdentity.Impersonate method.
If your application authenticates callers by using custom authentication, such as forms authentication, then you cannot impersonate the original caller through ASP.NET configuration. Instead you must call the Impersonate method of a WindowsIdentity object that you create for the original caller. You can obtain an impersonation token for the original caller, provided that the caller has a Windows account, by calling the Win32 LogonUser API (on Windows 2000 Server or Windows Server 2003) or by using a special form of the WindowsIdentity constructor that takes a single parameter of a user principal name (UPN). The technique you should use depends on the following:
In ASP.NET 2.0 applications, you can now change the default behavior to flow the impersonation token to newly created threads, when you create the thread with any of the managed threading techniques (such as Thread.Start, an asynchronous delegate, or QueueUserWorkItem). With .NET Framework 1.1, you have to manually propagate impersonation tokens to new threads you create.
The impersonation token does not propagate across threads if you use COM interop with components that have incompatible threading models, or if you use unmanaged techniques to create new threads (for example, when you use the Win32 CreateThread API). In these situations, the behavior is the same as in .NET Framework 1.1. For more information, see, PRB: 'Access Denied' Error Message Occurs When You Impersonate an Account in ASP.NET and then Call STA COM Components.
The most common situations where you might require impersonation and delegation are:
This How To shows you when and how to use each approach on Windows Server 2003 and on Windows 2000 Server.
If you need to access local resources, such as the file system or a local database, while assuming the identity of the original caller (the authenticated user), you need to use an appropriate IIS authentication type. Ideally, you should use IIS to configure your application's virtual directory for integrated Windows authentication, and then configure your ASP.NET application for Windows authentication and impersonation. This approach is shown in Figure 1.
Figure 1. Impersonating the original caller
This configuration is generally required if you need to control access to Windows resources with ACLs set against the individual user of your application.
To impersonate the original caller, you need to use IIS to configure appropriate authentication for your application's virtual directory and use appropriate configuration in the Web.config file.
To impersonate the original caller
Note While you could also select basic or digest authentication, integrated Windows authentication is recommended in this scenario.
<authentication mode="Windows" /> <identity impersonate="true" />
With this configuration, ASP.NET impersonates the IIS authenticated user, and the ASP.NET request threads that execute your Web application's code have the impersonation token attached to them. IIS passes a Windows token for the authenticated user to ASP.NET. The token can represent the authenticated user, if IIS is configured for Integrated Windows authentication, or another form of authentication such as basic, digest, or client certificate authentication. The token represents the anonymous user identity (IUSR_MACHINENAME) if IIS is configured to enable anonymous access.
While the above configuration ensures that all resource access is performed using the identity of the original caller, if you need to access specific resources such as local files by using the process identity, you can temporarily remove the impersonation token from the ASP.NET request thread by using the following code:
// Stop impersonation WindowsImpersonationContext ctx = WindowsIdentity.Impersonate(IntPtr.Zero); try { // Thread is now running under the process identity. // Any resource access here uses the process identity. } finally { // Resume impersonation ctx.Undo(); }
If you need to use the process identity for most resource access, and then impersonate the original caller to perform specific operations or to access specific resources, you need to use programmatic impersonation. (See Figure 2.)
Figure 2. Using programmatic impersonation to temporarily impersonate the original caller.
To temporarily impersonate the original caller within a particular method, you need to obtain the WindowsIdentity object that represents the authenticated user, and then call its Impersonate method.
To temporarily impersonate the original caller
<identity impersonate="false" />
Note By default, impersonation is disabled for all ASP.NET applications.
using System.Security.Principal; ... // Obtain the authenticated user's Identity WindowsIdentity winId = (WindowsIdentity)HttpContext.Current.User.Identity; WindowsImpersonationContext ctx = null; try { // Start impersonating ctx = winId.Impersonate(); // Now impersonating // Access resources using the identity of the authenticated user } // Prevent exceptions from propagating catch { } finally { // Revert impersonation if (ctx != null) ctx.Undo(); } // Back to running under the default ASP.NET process identity
This code impersonates the original caller. The original caller's identity is maintained in the HttpContext.Current.User.Identity object.
Notice how the preceding code uses a finally block to ensure that impersonation is reverted even in the event of an exception. The code also uses a catch block to ensure that an exception cannot propagate itself up the call stack while the thread is impersonating.
If your users have Windows domain accounts, but you are using custom authentication, such as forms authentication, IIS does not authenticate the caller and it passes a logon token that represents the anonymous user account to ASP.NET. To impersonate the caller in this instance, you must programmatically create a WindowsIdentity object for the caller, which you can use to impersonate. Create a WindowsIdentity object either by using a logon token returned from the Win32 LogonUser API, or by using the WindowsIdentity(userPrincipalName) constructor that takes a single parameter of a user principal name (UPN). The underlying extensions for the Kerberos protocol required by this constructor are only available on Windows Server 2003. For more information, see the section Impersonating by Using the WindowsIdentity Constructor in this document.
You can create a Windows token and associated logon session for a domain or local account by using the Win32 LogonUser API. You must pass the user name and password to this API, together with other parameters including the type of logon session you require.
Note You should protect the credentials passed to LogonUser by encrypting them.
You can impersonate with the returned token and check which Windows groups the user is a member of. Whether you can access local resources or network resources depends on the logon session type that you request (you specify the logon session type in the third argument of LogonUser). The most commonly used logon session types when calling this API are the following:
To check whether you have the Access this computer from the network user right
Note The name of the network logon session type sometimes causes confusion. It is called a network logon because it is equivalent to a user accessing the local computer from somewhere else on the network. The resulting logon session does not have network credentials, and therefore cannot be used to access network resources.
When you request an interactive logon, LogonUser returns a primary token that allows you to create processes while impersonating. When you request a network logon, LogonUser returns an impersonation token that can be used to access local resources, but not to create processes. If required, you can convert an impersonation token to a primary token by calling the Win32 DuplicateToken function.
The following example shows how to use the Win32 LogonUser API, construct a WindowsIdentity object from the token, attach the token to the current thread to begin impersonation, and then cancel the impersonation.
<%@ Page Language="C#" %> <%@ Import Namespace="System.Security.Principal" %> <%@ Import Namespace="System.Runtime.InteropServices" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> <script runat="server"> // Declare signatures for Win32 LogonUser and CloseHandle APIs [DllImport("advapi32.dll", SetLastError = true)] static extern bool LogonUser( string principal, string authority, string password, LogonSessionType logonType, LogonProvider logonProvider, out IntPtr token); [DllImport("kernel32.dll", SetLastError = true)] static extern bool CloseHandle(IntPtr handle); enum LogonSessionType : uint { Interactive = 2, Network, Batch, Service, NetworkCleartext = 8, NewCredentials } enum LogonProvider : uint { Default = 0, // default for platform (use this!) WinNT35, // sends smoke signals to authority WinNT40, // uses NTLM WinNT50 // negotiates Kerb or NTLM } protected void logonUserbtn_Click(object sender, EventArgs e) { IntPtr token = IntPtr.Zero; WindowsImpersonationContext impersonatedUser = null; try { // Create a token for DomainName\Bob // Note: Credentials should be encrypted in configuration file bool result = LogonUser("Bob", "DomainName", "P@ssw0rd", LogonSessionType.Network, LogonProvider.Default, out token); if (result) { WindowsIdentity id = new WindowsIdentity(token); // Begin impersonation impersonatedUser = id.Impersonate(); // Log the new identity Response.Write(String.Format( "</p>Identity after impersonation: {0}<br>", WindowsIdentity.GetCurrent().Name)); // Resource access here uses the impersonated identity } else { Response.Write("</p>LogonUser failed: " + Marshal.GetLastWin32Error().ToString()); } } catch { // Prevent any exceptions that occur while the thread is // impersonating from propagating } finally { // Stop impersonation and revert to the process identity if (impersonatedUser != null) impersonatedUser.Undo(); // Free the token if (token != IntPtr.Zero) CloseHandle(token); } // Verify the old process identity Response.Write(String.Format("</p>Identity after Undo: {0}<br>", WindowsIdentity.GetCurrent().Name)); } </script> <html > <head runat="server"> <title>LogonUser Test Page</title> </head> <body> <form id="form1" runat="server"> <div> <asp:Button ID="logonUserbtn" runat="server" OnClick="logonUserbtn_Click" Text="LogonUser" /></div> </form> </body> </html>
The preceding example passes LogonSessionType.Network to LogonUser. This results in a network logon session which has no network credentials. If you need a token and logon session that you can use to access network resources use LogonSessionType.Interactive. For more information, see Delegation in this document.
For clarity, the code example shown above uses literal strings to specify the credentials passed to LogonUser. If you use LogonUser, you should store the credentials that you pass to the method in encrypted format in your Web.config file. Store credentials in the <appSettings> section as shown in the following example, and then use the Aspnet_regiis.exe utility and a protected configuration provider to encrypt this section.
<appSettings> <add key="ImpIdentity" value="Domainname\Username;secretpassword"/> </appSettings>
The following example shows how to access this configuration data to pass to LogonUser.
string domain; string username; string password; // Access the <appSettings> value string[] userAndPassword = ((string)ConfigurationManager.AppSettings["ImpIdentity"]).Split(';'); // Parse out the domain, username and password domain = userAndPassword[0].Substring(0, userAndPassword[0].IndexOf(@"\")); userName = userAndPassword[0].Substring(userAndPassword[0].IndexOf(@"\")+1); password = userAndPassword[1]; // Call LogonUser bool result = LogonUser(userName, domain, password, LogonSessionTypes.Network, LogonProviders.Default, out token);
For more information about encrypting the configuration file sections, see:
When you use the LogonUser API for impersonation on Windows Server 2003, you do not need to grant your application's process identity the Act as part of the operating system user right.
On Windows Server 2003, instead of using LogonUser, you can generate a token by using the WindowsIdentity constructor. The advantage of this approach is that you do not have to store credentials on your Web server. The downside is that to get an impersonate-level token to use to access local resources, your process identity requires the Act as part of the operating system user right. For more information, see the section, Impersonating by Using the WindowsIdentity Constructor in this document.
When you use the LogonUser API for impersonation on Windows 2000 Server, you must grant the application's process account the Act as part of the operating system privilege. You should avoid this approach if possible to avoid raising the privileges of your Web application.
One of the overloads for the constructor on the WindowsIdentity class permits you to obtain a Windows token and logon session for a given domain account by supplying a user principal name (UPN). With this approach (shown in the following example), you do not need the account's password.
using System.Security.Principal; ... WindowsIdentity wi = new WindowsIdentity(userName@fullyqualifieddomainName); WindowsImpersonationContext ctx = null; try { ctx = wi.Impersonate(); // Thread is now impersonating } catch { // Prevent exceptions propagating. } finally { // Ensure impersonation is reverted ctx.Undo(); }
This WindowsIdentity constructor relies on a Windows Server 2003 extension to the Kerberos protocol called Service for User to Self (S4U2Self). You can use this approach if your application runs on a Windows Server 2003 in a Windows Server 2003 domain. The advantage of this approach is that you do not have to store credentials as you do for LogonUser. However, the disadvantage is that if your code needs to access local resources, you must grant the Act as part of the operating system privilege to your Web application process account to get an impersonation-level token.
To grant the Act as part of the operating system privilege
The type of token generated with the S4U2Self extension determines what you can do with the token while impersonating. You can obtain the following token types:
Note This places your process within the trusted computing base (TCB) of the Web server, which makes your Web server process very highly privileged. Where possible, you should avoid this approach because an attacker who manages to inject code and compromise your Web application will have almost unrestricted capabilities on the local computer.
Note You will typically need TCB when accessing a remote service. For local resources, the TCB privilege controls access. For remote service access, TCB is not required, but an identify-level token will usually be insufficient. Therefore, you will need TCB to generate an identify-level token. For example, if you use ADO.NET to communicate with a remote SQL Server database, the call will fail before it leaves the Web server due to the local resources such as performance counters (and therefore registry keys) that the SQL Server managed data provider needs to access.
If you need to impersonate the same identity throughout the lifetime of your application, you can specify credentials on the <identity> element in your Web.config file. The following example shows how to impersonate a Windows account with the name TestUser.
<identity impersonate="true" username="TestUser" password="P@ssw0rd" />
If you use this approach, you should encrypt the credentials. With ASP.NET version 2.0, you can use the Aspnet_regiis.exe tool. With ASP.NET version 1.1, you can use the Aspnet_setreg.exe tool.
To encrypt the <identity> element by using Aspnet_regiis
aspnet_regiis -pef "system.web/identity" " C:\Sites\IntranetSite"
To decrypt the <identity> element
aspnet_regiis -pdf "system.web/identity" " C:/Sites/IntranetSite "
Note that the above commands use the default protected configuration provider named, which uses RSA encryption. To choose an alternative provider, add the -prov option naming the required provider. If your application runs in a Web farm, you should generally use the RSA provider. For more information about how to use DPAPI and RSA encryption and advice about when to use machine-level key storage versus user-level key storage, see:
You can configure credentials on the <identity> element on Windows 2000 Server or Windows Server 2003 to use a fixed identity for resource access on ASP.NET applications. However, if you are running on Windows Server 2003 with IIS 6.0 configured to run in worker isolation mode (the default), you can avoid impersonation by configuring your ASP.NET application to run in a custom application pool that runs under a specific domain identity. For more information, see How To: Create a Service Account for an ASP.NET 2.0 Application.
To obtain an impersonation token so that you can access network resources, you have a number of options.
If you use domain accounts to run your Web application or the downstream service that you are accessing, you must also ensure that appropriate service principal names (SPNs) are created in Active Directory for those accounts. For more information, see How To: Use Protocol Transition and Constrained Delegation with ASP.NET 2.0.
Note that you must have access to both the user name and password to call LogonUser. You can only use the token to access network resources over a single hop, whereas Kerberos delegation allows the impersonated identity to flow across multiple tiers.
Use basic authentication if you cannot use Kerberos authentication and delegation, and you cannot use LogonUser or protocol transition. For example, if you configure IIS to use integrated Windows authentication, it will use Kerberos authentication if possible, but otherwise default to NTLM authentication—which does not allow access to network resources with an impersonated identity. If you cannot use the new WindowsIdentity constructor because you are not running on a Windows Server 2003 in a Windows 2003 domain, and you do not have access to the users password to call LogonUser, then basic authentication provides a solution. However, with basic authentication, the user's credentials pass through the network in clear text. Therefore, you should be sure that all network connections are secured with SSL or IPSEC.
When you design the authentication that you require in your application, consider whether to use impersonation to use the original caller's identity for access to back-end resources, or whether to use the trusted subsystem model, where the Web or application server is responsible for authenticating users and the server then uses a service identity to access back-end resources. The two techniques have different advantages and disadvantages as explained in the following subsections.
A trusted subsystem model is where the database server trusts the Web application identity. The Web application identity is trusted to make calls on behalf of the original caller. (See Figure 3.)
Figure 3. Trusted subsystem model
The advantages of the trusted subsystem model include:
The trusted subsystem model has the following disadvantages:
The impersonation and delegation model also has advantages and disadvantages.
The advantages of the impersonation / delegation model include:
The disadvantages of the impersonation / delegation model include:
The IIS authentication type that you use affects whether or not you can impersonate the original caller and use the impersonation token to access network resources. To do so requires delegation. Table 1 shows the various IIS authentication types and whether or not you can impersonate the caller to access network resources.
Table 1: IIS Authentication Types and Delegation Capability
Authentication Type | Can Delegate | Notes |
---|---|---|
Anonymous | Depends | If the anonymous account (by default IUSR_MACHINE) is configured in IIS as a local account, it cannot be delegated unless the local (Web server) and remote computer have identical local accounts (with matching user names and passwords). If the anonymous account is a domain account it can be delegated. |
Basic | Yes | If Basic authentication is used with local accounts, it can be delegated if the local accounts on the local and remote computers are identical. Domain accounts can also be delegated. |
Digest | No | |
Integrated Windows | Depends | Integrated Windows authentication either results in NTLM or Kerberos (depending upon the version of operating system on client and server computer). NTLM does not support delegation. Kerberos supports delegation with the appropriate Active Directory configuration. |
Client Certificates | Depends | Can be delegated if used with IIS certificate mapping and the certificate is mapped to a local account that is duplicated on the remote computer or is mapped to a domain account. This works because the credentials for the mapped account are stored on the local server and are used to create an Interactive logon session (which has network credentials). Active Directory certificate mapping does not support delegation. |
Keep the following in mind if you are using Windows 2000 Server.
Keep the following in mind if you are using Windows Server 2003:
For more information, see How To: Use Protocol Transition and Constrained Delegation.
Provide feedback by using either a Wiki or e-mail:
We are particularly interested in feedback regarding the following:
Technical support for the Microsoft products and technologies referenced in this guidance is provided by Microsoft Support Services. For product support information, please visit the Microsoft Support Web site at http://support.microsoft.com.
Community support is provided in the forums and newsgroups:
To get the most benefit, find the newsgroup that corresponds to your technology or problem. For example, if you have a problem with ASP.NET security features, you would use the ASP.NET Security forum.