Chapter 6 – Impersonation and Delegation in WCF

- J.D. Meier, Carlos Farre, Jason Taylor, Prashant Bansode, Steve Gregersen, Madhu Sundararajan, Rob Boucher

Objectives

  • Understand the different WCF authentication security options: message security, transport security, mixed, and both.
  • Understand the different authentication methods that are available when using these options.
  • Understand the WCF authorization options based on role, identity, and resource.
  • Understand the various WCF identities and the meaning of process identity, security principle, and ServiceSecurityContext.
  • Understand common implementation models that use specific authentication, authorization, and identity choices.

Overview

Impersonation is a common technique that WCF services use to assume the original caller’s identity in order to authorize access to service resources (such as files or database tables). Service resources can be resources that are either local to the service machine or remotely hosted. Impersonation is used to access resources on the same machine as the service, while delegation is used to access resources that are remotely hosted.
By default, impersonation is disabled and resources are accessed by using the WCF service’s process identity. Impersonation allows you to access local resources and perform other operations using the authenticated user's identity or a specific Windows identity. You can enable impersonation either programmatically or by applying appropriate attributes at operation or service levels.
You can impersonate imperatively or declaratively. Imperative impersonation is performed programmatically at run time and can vary depending on business logic or other conditions. Declarative impersonation is applied with a static attribute that can be associated with an operation or an entire interface. In general, you should use imperative impersonation when you need the fine granularity made possible by writing the impersonation logic into your code. If you do not need such fine granularity, you can use declarative impersonation.
Delegation allows you to use an impersonation token to access network resources. Your ability to use delegation depends on the authentication mechanism in use and appropriate account configuration.

Impersonation Scenarios

The most common impersonation and delegation scenarios are:
  • Impersonate the original caller. You want to access Windows resources that are protected with access control lists (ACLs) configured for your application’s domain user accounts.
  • Impersonate the original caller temporarily. You want to access resources predominantly by using the WCF service’s process identity, but specific methods need to use the original caller’s identity.
  • Impersonate a specific Windows identity. You need to use a specific identity or several Windows identities to access particular resources.
  • Use delegation to access network resources. You need to use an impersonated identity to access remote resources.

Impersonate the Original Caller

You will impersonate the original caller if you need to control access to Windows resources that are protected by ACLs that apply to the original callers of your service.
To impersonate the original caller for a specific operation or entire service, you need to use the declarative impersonation, for which you need to enable Windows authentication on the WCF service.
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 WCF request thread.

Impersonate the Original Caller Temporarily

If you need access to specific resources inside the scope of a service operation, you can use programmatic impersonation to limit the amount of time your service runs in the higher-privileged context of the original caller. There are three options for temporarily impersonating the original caller:
  • Use Windows authentication. To temporarily impersonate the original caller within a particular operation, you need to obtain the WindowsIdentity object that represents the authenticated user, and then call its Impersonate method. To use this option, you need to enable Windows authentication on the WCF service.
  • Use the WindowsIdentity constructor (S4U Kerberos extensions). Use this option if your users have Windows domain accounts, but you are using non-Windows authentication, such as certificate authentication. 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 by using the WindowsIdentity(userPrincipalName) constructor that takes a single parameter of a user principal name (UPN). With this approach, you do not need the account’s password.
  • Use the LogonUser API. Use this option if your users have Windows domain accounts, but you are using non-Windows authentication, such as custom authentication. 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 by using a logon token returned from the Win32 LogonUser API.

Impersonate a Specific Windows Identity

If you need to access local resources (such as the file system or a local database) while assuming the identity of the specific Windows account for the entire duration of the operation, configure the WCF service to run under that Windows identity.
If you need to use the process identity for most resource access, and then impersonate the specific Windows identity to perform specific operations or access specific resources, you can use programmatic impersonation. There are two options for impersonating a specific Windows identity temporarily:
  • Use the WindowsIdentity constructor (S4U Kerberos extensions). To impersonate the specific Windows identity, create a WindowsIdentity object by using the WindowsIdentity(userPrincipalName) constructor that takes a single parameter of a UPN. With this approach, you do not need the account’s password.
  • Use the LogonUser API. To impersonate the specific Windows identity, create a WindowsIdentity object by using a logon token returned from the Win32 LogonUser API.

Use Delegation to Access Network Resources

If your service needs to access remote / network resources, you can access the resources on behalf of the original caller or a fixed identity in the following ways:
  • Use Kerberos authentication and delegation. If you use Kerberos to authenticate your users, you can impersonate the original caller by using the techniques described in the sections “Impersonating the Original Caller” and “Impersonating the Original Caller Temporarily,” and then use Kerberos delegation to gain access to network resources as follows:
  • If your WCF service runs under the Network Service account, configure your computer account in Active Directory to be trusted for delegation.
  • If your application runs under a custom domain account, you must register a service principal name (SPN) in Active Directory to associate the domain account with the HTTP service on your WCF server. You then configure your domain account in Active Directory to be trusted for delegation.
  • Use protocol transition. With this approach, you use a non-Kerberos authentication mechanism, such as client certificates, to authenticate your users, and then use the new WindowsIdentity constructor to obtain a Windows token for the user on the server. Use this approach when you cannot use Kerberos authentication to authenticate your users. Keep in mind the following considerations:
  • If your WCF service runs under the Network Service account, configure your computer account in Active Directory to be trusted for delegation and protocol transition.
  • If your application runs under a custom domain account, you must register an SPN in Active Directory to associate the domain account with the HTTP service on your WCF server. You then configure your domain account in Active Directory to be trusted for delegation and protocol transition.
  • Call LogonUser and request an Interactive logon session. An interactive logon session has network credentials that allow you to authenticate against network servers. Use this approach when you cannot use Kerberos authentication to authenticate your users, and when you cannot use protocol transition.
Note that you must have access to both the username 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.

Impersonation Options

There are three options available for impersonation:
  • Impersonate using the WindowsIdentity token from Windows authentication.
  • Impersonate using the WindowsIdentity constructor (S4U Kerberos extensions).
  • Impersonate using the LogonUser API.

Impersonate Using the WindowsIdentity Token from Windows Authentication

With this option, you impersonate using the Windows token, obtained from the Security Support Provider Interface (SSPI) or Kerberos authentication, which is then cached on the service.
You can use this option when using Windows authentication or username authentication where users can be mapped to valid Windows accounts. This impersonation option supports programmatic and declarative impersonation in WCF.

Impersonate Using the WindowsIdentity Constructor (S4U Kerberos Extensions)

You can use this option when your clients are authenticated by using non-Windows authentication such as client certificates, which supports mapping to Windows accounts, or when you want to impersonate a service account. This impersonation option supports programmatic impersonation in WCF.
With this option, you need to create a WindowsIdentity object by using the WindowsIdentity(userPrincipalName) constructor that takes a single parameter of a UPN. With this approach, you do not need the account’s password.
The WindowsIdentity constructor relies on a Windows Server 2003 extension to the Kerberos protocol known as Service for User to Self (S4U2Self). You can use this approach if your application runs on 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.
Token Types
The type of token generated by the S4U2Self extension determines what you can do with the token while impersonating. You can obtain the following token types:
  • Identify-level token – This is returned by default. With this type of token, you can check to see what groups are contained in the token, but you cannot use it as an impersonation token to access local or remote resources.
  • Impersonation-level token – If you grant your process account the "Act as part of the operating system" user right, you get this type of token from the WindowsIdentity constructor. With this type of token, you can impersonate and access local resources.
Note: This places your process within the trusted computing base (TCB) of the WCF server, which makes your WCF service process very highly privileged. Where possible, you should avoid this approach because an attacker who manages to inject code and compromise your WCF application will have almost unrestricted capabilities on the local computer.
  • Delegate-level token – If you configure your service or machine account in Active Directory to be trusted for constrained delegation and protocol transition, you will get a token that you can use to access network resources.

Impersonate Using the LogonUser API

You can use this option when you want to access network resources (delegation) but you do not have the “trusted for delegation” permission. You can also use this option if you want to access local resources but you do not want to grant higher privileges to your WCF process identity. This places the responsibility for maintaining the user credentials on the WCF service. This impersonation option supports programmatic impersonation in WCF.
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.
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:
  • Interactive logon – If you need to access remote resources, request an interactive logon session. This results in a logon session that has network credentials. The user account passed to LogonUser must be granted the “Log on locally” user right.
  • Network logon – This establishes a logon session with no network credentials. This means you can impersonate the token and access local resources only. The user account passed to logon user must be granted the “Access this computer from the network” user right. By default, all accounts have this right because it is granted to the Everyone group.

Impersonation Methods

There are three methods used for impersonation:
  • Impersonate the original caller declaratively on specific operations.
  • Impersonate the original caller declaratively for the entire service.
  • Impersonate the original caller programmatically within an operation.

Impersonate the Original Caller Declaratively on Specific Operations

You can impersonate declaratively on an operation when you want to impersonate the original caller for the entire duration of a specific operation. Use impersonation selectively and only on the operations that need it, since by nature it increases the potential attack surface of your application.
You can impersonate declaratively by applying the OperationBehavior attribute on any operation that requires client impersonation. The following example shows how to impersonate for a specific operation.
[OperationBehavior(Impersonation = ImpersonationOption.Required)]
public string GetData(int value)
{
   return “test”;
}

Impersonate the Original Caller Declaratively for the Entire Service

Impersonate declaratively on a service when you want to impersonate the original caller for all of the operations in your service. However, you should be careful with this option because it can significantly increase the attack surface of your application by running all of your code under a higher-privileged account.
You can impersonate the entire service by applying the impersonateCallerForAllOperations attribute to "true" in the WCF configuration file. The following example shows how to impersonate for entire service:
...
<behaviors>
  <serviceBehaviors>
    <behavior name="ServiceBehavior">
      <serviceMetadata httpGetEnabled="true" />
      <serviceDebug includeExceptionDetailInFaults="false" />
      <serviceAuthorization impersonateCallerForAllOperations="true" />
    </behavior>
  </serviceBehaviors>
</behaviors>
...

If you are impersonating all operations in the service, the Impersonation property of the OperationBehaviorAttribute applied to each operation will be overriden. Therefore if the property on the operation is set to something other than Allowed or Required, impersonation will be turned off for that operation.
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

Impersonate programmatically when you want to impersonate the original caller for a short duration in a service operation. You can impersonate programmatically by calling the Impersonate() method on the Windows identity that you want to impersonate, as follows:
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 (DoS) or elevation of privilege attacks. In the example above, the using statement ensures that the impersonation is reverted after execution of the using block.

Controlling Impersonation on the Service Side

You can control impersonation on the service side by using declarative impersonation. You can use the ImpersonationOption enumeration along with the OperationBehaviorAttribute attribute to control impersonation. The following impersonation options are available:
  • NotAllowed – Impersonation is not performed in a particular operation.
  • Allowed – Impersonation is performed if the original Windows token is available and the service is configured to impersonate on all operations using the ImpersonateCallerForAllOperations in the ServiceAuthorizationBehavior attribute.
  • Required – Impersonation is performed; the Windows identity token is required to be available.
The following example uses declarative impersonation to control impersonation on the service side:
[OperationBehavior(Impersonation = ImpersonationOption.Required)]
public string GetData(int value)
{
   return “test”;
}

Controlling Impersonation on the Client Side

You can control impersonation on the client side and prevent WCF services from using client identities to access local resources. Windows credentials have an AllowedImpersonationLevel property that is set to one of the following TokenImpersonationLevel options in order to control the impersonation level:
  • None – The WCF service cannot authenticate or impersonate the user.
  • Anonymous – The WCF service authenticates clients as anonymous, but cannot obtain information for identification or impersonation.
  • Identification – The WCF service can authenticate clients and get information for identification, but cannot impersonate the clients. This is the default value.
  • Impersonation – The WCF service can authenticate, get information for identification, and impersonate clients on local systems.
  • Delegation – The WCF service can authenticate, get information for identification, and impersonate clients on local as well as remote systems.
The following example shows how to configure the impersonation level on the client side:
<behaviors>
    <endpointBehaviors>
        <behavior name="NewBehavior">
            <clientCredentials>
                <windows allowedImpersonationLevel="Impersonation" />
            </clientCredentials>
        </behavior>
    </endpointBehaviors>
</behaviors>

Note: The impersonation level obtained by the server when it impersonates the client token is not solely a function of this setting. It is also a function of the associated privileges and domain settings for the account in which the service is running.

Pitfalls and Issues with Impersonation

Impersonation and delegation are powerful tools that should be used cautiously and selectively. Improper use of impersonation and delegation can easily lead to serious security vulnerabilities. The following are examples of common mistakes to avoid:
  • Using impersonation by default – Only use impersonation when you need to. Using impersonation can introduce security vulnerabilities, particularly in multi-threaded applications. The use of impersonation prevents the efficient use of connection pooling if you are accessing downstream databases using the impersonated identity. Be diligent when choosing to impersonate and think about other alternatives; for example, if downstream auditing is your goal, you might choose to pass the user identity as part of the method calls rather than using delegation.
  • Using callbacks when impersonating – Avoid using callbacks when impersonating, as you do not have control over what code will be executed under the impersonated identity.
  • Failure to revert impersonation – When using programmatic impersonation, be sure to explicitly revert the impersonation. If you do not remember to revert, your application’s attack surface will be increased because it will continue to run under higher privileges.
  • Using the WindowsIdentity constructor (S4U Kerberos extensions) – Using S4U is sometimes necessary but requires you to place your process within the trusted computing base (TCB) of the WCF server. This has the side effect of making your WCF service process very highly privileged. Where possible, you should avoid this approach because an attacker who manages to inject code and compromise your WCF application will have almost unrestricted capabilities on the local computer.
  • Using the LogonUser API – Using LogonUser requires you to maintain user credentials on the WCF service. If you must use this API, be sure to protect the credentials passed to LogonUser by encrypting the credentials.

Related Items

  • For more information, see “How To – Use Delegation for Flowing the Original Caller Credentials to Back-end in WCF Calling from Windows Forms.”
  • For more information, see “How To – Use Protocol Transition for Impersonating and Delegating the Original Caller in WCF.”

Additional Resources

Last edited Jun 11, 2008 at 8:07 PM by prashantbansode, version 1

Comments

mhidalgo Nov 25, 2010 at 9:22 PM 
Hi, Great article

I have a question :What if I want to set network credentials in the client side, for example If I host the WCF service in IIS, so I created a proxy to consume this service. I set network credentials to consume the service.
I have a question for you: How can I read this values in the Service side?

Thanks