SECURITY

Programming WCF Services : Security - Intranet Application Scenario (part 5) - Impersonation - Impersonating all operations, Restricting impersonation

10/15/2013 7:44:36 PM
6.3. Impersonating all operations

In the event that you need to enable impersonation in all the service operations, the ServiceHostBase class has the Authorization property of the type ServiceAuthorizationBehavior:

public abstract class ServiceHostBase : ...
{
public ServiceAuthorizationBehavior Authorization
{get;}
//More members
}
public sealed class ServiceAuthorizationBehavior : IServiceBehavior
{
public bool ImpersonateCallerForAllOperations
{get;set;}
//More members
}

ServiceAuthorizationBehavior provides the Boolean property ImpersonateCallerForAllOperations, which is false by default. Contrary to what its name implies, when set to true, this property merely verifies that the service does not have any operations configured with ImpersonationOption.NotAllowed. This constraint is verified at service load time, yielding an InvalidOperationException when violated.

In effect, when Windows authentication is used, this will amount to the service automatically impersonating the client in all operations, but all the operations must be explicitly decorated with ImpersonationOption.Allowed or ImpersonationOption.Required. ImpersonateCallerForAllOperations has no effect on constructors.

You can set the ImpersonateCallerForAllOperations property programmatically or in the config file. If you set it programmatically, you can do so only before opening the host:

ServiceHost host = new ServiceHost(typeof(MyService));
host.Authorization.ImpersonateCallerForAllOperations = true;
host.Open();

If you set it using a config file, you need to reference the matching service behavior in the service declaration:

<services>
<service name = "MyService" behaviorConfiguration= "ImpersonateAll">
...
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name = "ImpersonateAll">
<serviceAuthorization impersonateCallerForAllOperations = "true"/>
</behavior>
</serviceBehaviors>
</behaviors>

To automate impersonating in all operations without the need to apply the OperationBehavior attribute on every method, I wrote the SecurityHelper static class, with the ImpersonateAll() extension methods:

public static class SecurityHelper
{
public static void ImpersonateAll(this ServiceHostBase host);
public static void ImpersonateAll(this ServiceDescription description);
//More members
}

The extension methods work on both ServiceHost and ServiceHost<T>.

You can only call ImpersonateAll() before opening the host:

//Will impersonate in all operations
class MyService : IMyContract
{
public void MyMethod()
{...}
}
ServiceHost host = new ServiceHost(typeof(MyService));
host.ImpersonateAll();
host.Open();

Example 3 shows the implementation of ImpersonateAll().

Example 3. Implementing SecurityHelper.ImpersonateAll()
public static class SecurityHelper
{
public static void ImpersonateAll(this ServiceHostBase host)
{
host.Authorization.ImpersonateCallerForAllOperations = true;
host.Description.ImpersonateAll();
}
public static void ImpersonateAll(this ServiceDescription description)
{
foreach(ServiceEndpoint endpoint in description.Endpoints)
{
if(endpoint.Contract.Name == "IMetadataExchange")
{
continue;
}
foreach(OperationDescription operation in endpoint.Contract.Operations)
{
OperationBehaviorAttribute attribute = operation.Behaviors.
Find<OperationBehaviorAttribute>();
attribute.Impersonation = ImpersonationOption.Required;
}
}
}
//More members
}


In Example 3, ImpersonateAll() (for the sake of good manners) first sets the ImpersonateCallerForAllOperations property of the provided host to true, then obtains the service description from the host and calls the other overloaded extension method of ServiceDescription. This version explicitly configures all operations with ImpersonationOption.Required, by iterating over the endpoints collection of the service description. For each endpoint (except the metadata exchange endpoints), ImpersonateAll() accesses the operations collection of the contract. For each operation, there is always exactly one OperationBehaviorAttribute in the collection of operation behaviors, even if you did not provide one explicitly. The method then simply sets the Impersonation property to ImpersonationOption.Required.

6.4. Restricting impersonation

Authorization and authentication protect the service from being accessed by unauthorized, unauthenticated, potentially malicious clients. However, how should the client be protected from malicious services? One of the ways an adversarial service could abuse the client is by assuming the client’s identity and credentials and causing harm while masquerading as the client. This tactic enables the malicious service both to leave an identity trail pointing back to the client and to elevate its own potentially demoted, less-privileged credentials to the client’s level.

In some cases, the client may not want to allow the service to obtain its identity at all. WCF therefore lets the client indicate the degree to which the service can obtain the client’s identity and how it can use it. Impersonation is actually a range of options indicating the level of trust between the client and the service. The WindowsClientCredential class provides the AllowedImpersonationLevel enum of the type TokenImpersonationLevel, found in the System.Security.Principal namespace:

public enum TokenImpersonationLevel
{
None,
Anonymous,
Identification,
Impersonation,
Delegation
}
public sealed class WindowsClientCredential
{
public TokenImpersonationLevel AllowedImpersonationLevel
{get;set;}
//More members
}

The client can use AllowedImpersonationLevel to restrict the allowed impersonation level both programmatically and administratively. For example, to programmatically restrict the impersonation level to TokenImpersonationLevel.Identification, before opening the proxy the client would write:

MyContractClient proxy = new MyContractClient();
proxy.ClientCredentials.Windows.AllowedImpersonationLevel =
TokenImpersonationLevel.Identification;
proxy.MyMethod();
proxy.Close();


When using a config file, the administrator should define the allowed impersonation level as a custom endpoint behavior and reference it from the relevant endpoint section:

<client>
<endpoint behaviorConfiguration = "ImpersonationBehavior"
...
/>
</client>
<behaviors>
<endpointBehaviors>
<behavior name = "ImpersonationBehavior">
<clientCredentials>
<windows allowedImpersonationLevel = "Identification"/>
</clientCredentials>
</behavior>
</endpointBehaviors>
</behaviors>

TokenImpersonationLevel.None simply means that no impersonation level is assigned, so the client provides no identity information. This setting therefore amounts to the same thing as TokenImpersonationLevel.Anonymous, where the client provides no credentials at all. These two values are, of course, the safest from the client’s perspective, but they are the least useful options from the application’s perspective, since the service cannot perform any authentication or authorization. Not sharing credentials is possible only if the service is configured for anonymous access or for having no security, which is not the case with the intranet scenario. If the service is configured for Windows security, these two values yield an ArgumentOutOfRangeException on the client side.

With TokenImpersonationLevel.Identification, the service can identify the client (i.e., obtain the security identity of the calling client). The service, however, is not allowed to impersonate the client—everything the service does must be done under the service’s own identity. Trying to impersonate will throw an ArgumentOutOfRangeException on the service side. Note, however, that if the service and the client are on the same machine, the service will still be able to impersonate the client, even when TokenImpersonationLevel.Identification is used. TokenImpersonationLevel.Identification is the default value used with Windows security and is the recommended value for the intranet scenario.

TokenImpersonationLevel.Impersonation grants the service permission both to obtain the client’s identity and to impersonate the client. Impersonation indicates a great deal of trust between the client and the service, since the service can do anything the client can do, even if the service host is configured to use a less privileged identity. The only difference between the real client and the impersonating service is that if the service is on a separate machine from the client, it cannot access resources or objects on other machines as the client, because the service machine does not really have the client’s password. In the case where the service and the client are on the same machine, the service impersonating the client can make one network hop to another machine, since the machine it resides on can still authenticate the impersonated client identity.

Finally, TokenImpersonationLevel.Delegation provides the service with the client’s Kerberos ticket. In this case, the service can freely access resources on any machine as the client. If service is also configured for delegation, when it calls other downstream services the client’s identity could be propagated further and further down the call chain. Delegation-required Kerberos authentication is not possible on Windows workgroup installations. Both the client and server user accounts must be properly configured in Active Directory to support delegation, due to the enormous trust (and hence security risk) involved. Delegation uses by default another security service called cloaking, which propagates the caller’s identity along the call chain.

Delegation is extremely dangerous from the client’s perspective, since the client has no control over who ends up using its identity, or where. When the impersonation level is set to TokenImpersonationLevel.Impersonation, the client takes a calculated risk: it knows which services it is accessing, and if those services are on a different machine, the client identity cannot propagate across the network. I consider delegation something that enables the service not just to impersonate the client, but to act as an imposter; security-wise, as far as the client is concerned, this is tantamount to waiving security.

6.5. Avoiding impersonation

You should design your services so that they do not rely on impersonation, and your clients should use TokenImpersonationLevel.Identification. Impersonation is a relic of the ’90s, typically used in classic two-tier systems in the absence of role-based security support, where scalability was not a concern and managing a small number of identities across resources was doable.

As a general design guideline, the further down the call chain from the client, the less relevant the client’s identity is. If you use some kind of layered approach in your system design, each layer should run under its own identity, authenticate its immediate callers, and implicitly trust its calling layer to authenticate its callers, thereby maintaining a chain of trusted, authenticated callers. This is called the trusted subsystem pattern. Impersonation, on the other hand, requires you to keep propagating the identity further and further down the call chain, all the way to the underlying resources. Doing so impedes scalability, because many resources (such as SQL Server connections) are allocated per identity. With impersonation, you will need as many resources as clients, and you will not be able to benefit from resource pooling (such as connection pooling). Impersonation also complicates resource administration, because you need to grant access to the resources to all of the original client identities, and there could be numerous such identities to manage. A service that always runs under its own identity poses no such problems, regardless of how many identities access that service. To control access to the resources, you should use authorization, as discussed next.

Multitier systems that do use impersonation typically gravitate toward delegation, since that is the only way to propagate the client identities across tiers and machines. In fact, the main reason developers today use impersonation has little to do with resource access authorization (which can easily be accomplished with role-based security); instead, it is used as a mechanism for auditing and identity propagation. If the application is required to provide at lower layers the identity of the topmost client or all clients up the chain, impersonation (if not full-fledged delegation) may look like a viable option. There are three good solutions for these requirements. First, if the business use cases require you to provide the top-level identity to downstream parties, there is nothing wrong with providing it as explicit method arguments since they are part of the required behavior of the system. The second solution is to use security audits (discussed later) and leave a trail across the call chain. At any point, you can reconstruct that chain of identities from the local audits. The third option is to propagate the identity of the original caller (or the entire stack of callers) in the message headers.

Finally, relying on impersonation precludes non-Windows authentication mechanisms. If you do decide to use impersonation, use it judiciously and only as a last resort, when there is no other, better design approach.


Note:

Impersonation is not possible with queued services.
Other  
 
Top 10
Review : Sigma 24mm f/1.4 DG HSM Art
Review : Canon EF11-24mm f/4L USM
Review : Creative Sound Blaster Roar 2
Review : Philips Fidelio M2L
Review : Alienware 17 - Dell's Alienware laptops
Review Smartwatch : Wellograph
Review : Xiaomi Redmi 2
Extending LINQ to Objects : Writing a Single Element Operator (part 2) - Building the RandomElement Operator
Extending LINQ to Objects : Writing a Single Element Operator (part 1) - Building Our Own Last Operator
3 Tips for Maintaining Your Cell Phone Battery (part 2) - Discharge Smart, Use Smart
REVIEW
- First look: Apple Watch

- 3 Tips for Maintaining Your Cell Phone Battery (part 1)

- 3 Tips for Maintaining Your Cell Phone Battery (part 2)
VIDEO TUTORIAL
- How to create your first Swimlane Diagram or Cross-Functional Flowchart Diagram by using Microsoft Visio 2010 (Part 1)

- How to create your first Swimlane Diagram or Cross-Functional Flowchart Diagram by using Microsoft Visio 2010 (Part 2)

- How to create your first Swimlane Diagram or Cross-Functional Flowchart Diagram by using Microsoft Visio 2010 (Part 3)
Popular Tags
Microsoft Access Microsoft Excel Microsoft OneNote Microsoft PowerPoint Microsoft Project Microsoft Visio Microsoft Word Active Directory Biztalk Exchange Server Microsoft LynC Server Microsoft Dynamic Sharepoint Sql Server Windows Server 2008 Windows Server 2012 Windows 7 Windows 8 Adobe Indesign Adobe Flash Professional Dreamweaver Adobe Illustrator Adobe After Effects Adobe Photoshop Adobe Fireworks Adobe Flash Catalyst Corel Painter X CorelDRAW X5 CorelDraw 10 QuarkXPress 8 windows Phone 7 windows Phone 8