ASP.NET 2.0 Security Guidelines - Impersonation/Delegation

From Guidance Share
Jump to navigationJump to search

- J.D. Meier, Alex Mackman, Blaine Wastell, Prashant Bansode, Andy Wigley, Kishore Gopalan

Know Your Tradeoffs with Impersonation

Be aware that impersonation prevents the efficient use of connection pooling if you access downstream databases by using the impersonated identity. This impacts the ability of your application to scale. Also, using impersonation can introduce other security vulnerabilities, particularly in multi-threaded applications, such as ASP.NET Web applications.

You might need impersonation if you need to:

  • Flow the original caller's security context to the middle tier and/or data tier of your Web application to support fine-grained (per-user) authorization.
  • Flow the original caller's security context to the downstream tiers to support operating system level auditing.
  • Access a particular network resource by using a specific identity.

Avoid Calling LogonUser

Avoid writing code that calls the LogonUser API to create impersonation tokens because this forces you to store user names and passwords on your Web server.

Instead, use a unique application pool with a specific process identity if you need a specific identity to access downstream resources. If you need multiple identities to access a range of downstream resources and services, use Windows Server 2003 protocol transition and the new WindowsIdentity constructor; this allows you to create a Windows token that is given only an account's user principal name (UPN). To access a network resource, you need to delegate-level token. To get this token type, your server needs to be configured as trusted for delegation in Active Directory.

The following code shows how to use this constructor to obtain a Windows token for a given user.

using System;
using System.Security.Principal;
public void ConstructToken(string upn, out WindowsPrincipal p)
 WindowsIdentity id = new WindowsIdentity(upn);
 p = new WindowsPrincipal(id);

Note An account's UPN is guaranteed to be unique within a forest. Frequently, the UPN is the user's e-mail address, but it does not have to be. A user always has a UPN and by default, it is userlogonname@fullyqualifieddomainname. If you are logged into a domain, you can find your UPN name by running whoami /upn from a command window. For more information, see How To: Use Protocol Transition and Constrained Delegation with ASP.NET 2.0.

Avoid Programmatic Impersonation Where Possible

If programmatic impersonation is not done properly, it can introduce security vulnerabilities. It is difficult to get it correct, particularly in multithreaded applications. When possible, use alternative approaches, such as a custom domain process identity for resource access. You should avoid programmatic impersonation where possible for the following reasons:

  • It is easy to introduce errors because of thread switches where the thread impersonation token is not propagated across threads.
  • Some programmatic techniques require you to store credentials which should be avoided.
  • Some programmatic techniques require you to grant additional privileges to your process account, which you should avoid. For example, you must grant your process account "Act as part of the operating system" if you call LogonUser on Windows Server 2000 or to obtain an impersonate-level token on Windows Server 2003 when you use the new WindowsIdentity constructor that generates a token from a user principal name.
  • If exceptions occur while impersonating, it is possible for malicious code higher in the call stack to run using the impersonated identity. This can present security issues, particularly if you impersonate a highly privileged account.

If You Need to Impersonate, Consider Threading Issues

Losing an impersonated security context because of thread switches is a common vulnerability. The common language runtime (CLR) automatically propagates impersonation tokens to threads that you create using any of the managed threading techniques, such as Thread.Start, an asynchronous delegate, or QueueUserWorkItem. However, it is easy to drop the thread impersonation token if you use COM interop with components that have incompatible threading models or if you use unmanaged techniques to create new threads such as the Win32 CreateThread API.

If You Need to Impersonate, Clean Up Appropriately

If you must use programmatic impersonation, use structured exception handling and put the impersonation code inside try blocks. Use a catch block to handle exceptions and use a finally block to ensure that the impersonation is reverted as shown here.

using System.Security.Principal;
WindowsIdentity winIdentity = new WindowsIdentity("username@domainName");
WindowsImpersonationContext ctx = winIdentity.Impersonate();
 // Do work
catch(Exception ex)
 // Stop impersonating
 // Stop impersonating

By using a finally block, you ensure that the impersonation token is removed from the current thread whether an exception is generated or not. Also be aware that if your code fails to catch exceptions, a malicious user could use exception filters to execute code that runs under the impersonated security context. This is particularly serious if your code impersonates a privileged account. If your code does not catch the exception, exception filters higher in the call stack are executed before code in your finally block is executed.

Note Exception filters are supported by Microsoft Intermediate Language (MSIL) and Visual Basic .NET. For more information, see How To: Use Impersonation and Delegation in ASP.NET 2.0.

Avoid Losing Impersonation Tokens

In .NET Framework 1.1, impersonation tokens did not automatically flow to newly created threads. This situation could lead to security vulnerabilities because new threads assume the security context of the process. In ASP.NET 2.0 applications you can now change this default behavior by configuring the ASPNET.config file in the %Windir%Microsoft.NET\Framework\{Version} directory.

If you need to flow the impersonation token to new threads, set the enabled attribute to true on the alwaysFlowImpersonationPolicy element in the ASPNET.config file, as shown in the following example.

   <alwaysFlowImpersonationPolicy enabled="true"/>

If you need to prevent impersonation tokens from being passed to new threads programmatically, you can use the ExecutionContext.SuppressFlow method.