Impersonation/Delegation

patterns & practices Developer Center

By default, WCF applications do not impersonate, although in some scenarios you need to use impersonation to perform operations or to access resources using the authenticated user's identity.

If you use impersonation, consider the following guidelines:

  • Know the tradeoffs involved in impersonation.
  • Know your impersonation options.
  • Know your impersonation methods.
  • Consider using programmatic instead of declarative impersonation.
  • When impersonating programmatically, be sure to revert to the original context.
  • When impersonating declaratively, only impersonate on the operations that require it.
  • When you cannot do a Windows mapping, consider using the S4U feature for impersonation and delegation.
  • If your WCF service cannot be trusted for delegation, consider using the LogonUser API.
  • If you have to flow the original caller to the back-end services, use constrained delegation.

Each of these guidelines is described in the following sections.

Know the tradeoffs involved in 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.

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 in order to support fine-grained (per-user) authorization.
  • Flow the original caller's security context to the downstream tiers in order to support operating system—level auditing.
  • Access a particular network resource by using a specific identity.

Know your impersonation options

Impersonation is used to restrict or authorize the original caller's access to a WCF service's local resources, such as files. There are three options available for impersonation:

  • Impersonate using Windows authentication.
  • Impersonate using S4U Kerberos extensions.
  • Impersonate using the LogonUser API.

Each of these options is described in the following sections.

Impersonate using Windows Authentication

With this option, you impersonate by using the Windows token, which is obtained from the Security Support Provider Interface (SSPI) or Kerberos authentication, or any other authentication type that can map to Windows, such as username or certificate authentication. The Windows identity token obtained by this method is then cached on the service.

This impersonation option supports programmatic and declarative impersonation in WCF.

Impersonate using S4U Kerberos extensions

With this option, you impersonate by using a Windows token obtained from the Kerberos extensions, collectively called Service-for-User (S4U). You can use this option when your clients are authenticated using non-Windows authentication types such as client certificates but have mapping to Windows accounts, or when you want to impersonate a service account. This impersonation option supports programmatic impersonation in WCF.

Note

To impersonate at the impersonation level, you must grant your process account the “Act as part of the operating system” user right.

Impersonate using the LogonUser API

With this option, you impersonate by using a Windows token obtained from the LogonUser Windows API. You can use this option when you want to access network resources (delegation) but do not have trust for delegation, or if you want to access local resources but do not want to grant higher privileges to your WCF process identity. This option adds the responsibility of maintaining the user credentials on the WCF service. This impersonation option supports programmatic impersonation in WCF.

Additional Resources

Know your impersonation methods

Impersonation is used to restrict or authorize the original caller's access to a WCF service's local resources, such as files. There are three methods of impersonation:

  • Impersonate the original caller declaratively on specific operations.
  • Impersonate the original caller declaratively on the entire service.
  • Impersonate the original caller programmatically within an operation.

Impersonate the original caller declaratively on specific operations

Use this option when you want to impersonate the original caller for the entire duration of a specific operation. Impersonation is a costly operation and is usually used for higher-privileged original callers, hence you would use impersonation selectively only on the operations that need a reduced potential attack surface. You can impersonate declaratively by applying the OperationBehaviorAttribute attribute on any operation that requires client impersonation, as shown in the following code example:

[OperationBehavior(Impersonation = ImpersonationOption.Required)]
public string GetData(int value)
{
   return “test”;
}

Impersonate the original caller declaratively on the entire service

Use this option when you want to impersonate the original caller for the entire duration of all the operations. Impersonation is a costly operation and is usually used for higher-privileged original callers. You need to be careful when using this option because it potentially increases the attack surface. To impersonate the entire service, set the impersonateCallerForAllOperations attribute to "true" in the WCF configuration file, as shown in the following example:

...
<behaviors>
  <serviceBehaviors>
    <behavior name="ServiceBehavior">
      <serviceMetadata httpGetEnabled="true" />
      <serviceDebug includeExceptionDetailInFaults="false" />
      <serviceAuthorization impersonateCallerForAllOperations="true" />
    </behavior>
  </serviceBehaviors>
</behaviors>
...

When impersonating for all operations, the Impersonation property of the OperationBehaviorAttribute applied to each method must also be set to either Allowed or Required.

Note

When a service has higher credentials than the remote client, the credentials of the service are used if the Impersonation property is set to Allowed. That is, if a low-privileged user provides its credentials, a higher-privileged service executes the method with the credentials of the service, and can use resources that the low-privileged user would otherwise not be able to use.

Impersonate the original caller programmatically within an operation

Use this option when you want to impersonate the original caller for a short duration in a service operation. Impersonation is a costly operation and is usually used for higher-privileged original callers, hence you would use impersonation only when needed to reduce the potential attack surface. Perform programmatic impersonation as shown in the following example:

public string GetData(int value)
{
 using (ServiceSecurityContext.Current.WindowsIdentity.Impersonate())
 {
     // return the impersonated user (original users identity)
     return string.Format("Hi, {0}, you have entered: {1}",
          WindowsIdentity.GetCurrent().Name, value);
 }   
}

Note

It is important to revert to impersonation. Failure to do so can form the basis for denial of service and elevation of privilege attacks. In the example above, the using statement ensures that the impersonation is reverted after execution of the using block.

Additional Resources

Consider using programmatic instead of declarative impersonation

Use programmatic impersonation to impersonate the original caller or the ASP.NET service account calling into your service. Programmatic impersonation allows you to impersonate on specific lines of code rather than the entire operation. Although this finer-grained approach to impersonation can reduce security risk, be aware that it is easier to make a mistake during implementation that could leave your code impersonating at higher privilege in the event of an error. Use the using statement to revert impersonation automatically.

The following code snippet shows how to impersonate programmatically:

public string GetData(int value)
{
 using (ServiceSecurityContext.Current.WindowsIdentity.Impersonate())
 {
     // return the impersonated user (original users identity)
     return string.Format("Hi, {0}, you have entered: {1}",
          WindowsIdentity.GetCurrent().Name, value);
 }   
}

Additional Resources

When impersonating programmatically, be sure to revert to the original context

When using programmatic impersonation, revert to the original security context as soon as possible. If you do not remember to revert, your application's attack surface will be increased because it will be running under higher privileges than necessary. Use the using statement to revert impersonation automatically.

The following code snippet shows how to impersonate programmatically:

public string GetData(int value)
{
 using (ServiceSecurityContext.Current.WindowsIdentity.Impersonate())
 {
     // return the impersonated user (original users identity)
     return string.Format("Hi, {0}, you have entered: {1}",
          WindowsIdentity.GetCurrent().Name, value);
 }
}

Additional Resources

When impersonating declaratively, only impersonate on the operations that require it

Only impersonate on specific operations that require it. If you impersonate on operations that do not require the additional privileges, you will increase your attack surface as well as the potential impact of an exploit.

Impersonation is a costly operation and is usually used for higher-privileged original callers. Use impersonation selectively only on the operations that need to reduce the potential attack surface. You can impersonate declaratively by applying the OperationBehaviorAttribute attribute on any operation that requires client impersonation, as shown in the following code example:

[OperationBehavior(Impersonation = ImpersonationOption.Required)]
public string GetData(int value)
{
   return “test”;
}

Additional Resources

When you cannot do a Windows mapping, consider using the S4U feature for impersonation and delegation

In many situations—for example, if your users access a WCF service over the Internet—you cannot use Kerberos authentication because firewalls prevent the client computer from directly communicating with the domain controller. Instead, your service must authenticate the client by using another approach, such as username authentication, or in an extranet scenario, client certificate authentication.

In such situations where you cannot map the username or certificate authentication directly to Windows accounts, you can consider using the protocol transition (S4U) feature that permits applications to use a non-Windows authentication mechanism to authenticate users, while still using Kerberos authentication and delegation to access downstream network resources. This allows your application to access downstream servers that require Windows authentication, and it allows you to use Windows auditing to track user access to back-end resources.

Use the WindowsIdentity constructor to create a Windows token giving only an account's user principal name (UPN).

Note

Important: To impersonate at the impersonation level, you must grant your process account the "Act as part of the operating system" user right. To impersonate at the delegation level, you must enable protocol transition in Active Directory in order to access network resources.

The following code snippet 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);
}

Additional Resources

If your WCF service cannot be trusted for delegation, consider using the LogonUser API

Use the Win32 LogonUser API (via P/Invoke) to create delegation-level impersonation tokens, but only when your WCF service cannot be trusted for delegation, because this option forces you to store usernames and passwords on your WCF service.

Use the Basic authentication mode to get the original user's impersonation token so that you will have access to the username and password. You can then get the impersonation token by using the LogonUser API. For service accounts, you will have to store the username and password securely, and then use the LogonUser API to get the impersonation token.

The following code example shows how the LogonUser API is used for impersonation:

using System.Runtime.InteropServices;
…
// Declare the logon types as constants
const long LOGON32_LOGON_NETWORK = 3;

// Declare the logon providers as constants
const long LOGON32_PROVIDER_DEFAULT = 0;

[DllImport("advapi32.dll",EntryPoint = "LogonUser")]
private static extern bool LogonUser(
           string lpszUsername,
           string lpszDomain,
           string lpszPassword,
           int dwLogonType,
           int dwLogonProvider,
           ref IntPtr phToken);
[DllImport("kernel32.dll", CharSet=CharSet.Auto)]
public extern static bool CloseHandle(IntPtr handle);

private void ImpersonateAndUse(string Username,
                                        string Password,
                                        string Domain)
{
  IntPtr token = new IntPtr(0);
  token = IntPtr.Zero;
  // Call LogonUser to obtain a handle to an access token.
  bool returnValue = LogonUser(Username, Domain,Password,
                                 (int)LOGON32_LOGON_NETWORK,
                                 (int)LOGON32_PROVIDER_DEFAULT,
                                 ref token);
  if (false == returnValue)
  {
     int ret = Marshal.GetLastWin32Error();
     string strErr = String.Format("LogonUser failed with error code : {0}", ret);
     throw new ApplicationException(strErr, null);
  }
  WindowsIdentity newId = new WindowsIdentity(token);
  WindowsImpersonationContext impersonatedUser = newId.Impersonate();
  try
  {
     // do the operations using original user security context
  }
  finally
  {
     // stop impersonating
     impersonatedUser.Undo();
     CloseHandle(token);
  }
}

Additional Resources

If you have to flow the original caller to the back-end services, use constrained delegation

Use delegation for flowing the impersonated original user's security context (Windows identity) to the remote back-end service. On the remote back-end service, the original user's Windows identity can be used to authenticate or impersonate the original caller, in order to restrict or authorize the original caller's access to local resources.

When using delegation on Microsoft Windows Server® 2003 or later, use constrained delegation. This allows administrators to specify exactly which services on a downstream server or a domain account can be accessed when using an impersonated user's security context.

Additional Resources