Securing Workflow Services

Exploring Ways You Can (and Can’t) Secure Workflow Services

Exploring WCF Web Services

LANGUAGES: C#

TECHNOLOGIES: .NET 3.5 | WCF

 

Securing Workflow Services

Exploring Ways You Can (and Can t) Secure Workflow Services

 

By Michele Leroux Bustamante

 

This month we ll explore how to secure calls to a Workflow Service and call from a Workflow Service to downstream WCF services, as well as other related issues. If you have questions about migrating your existing ASMX or WSE Web services to WCF, or questions regarding WCF as it relates to Web services, please send them to [email protected]!

 

Last month I provided an overview of Workflow Services, a feature of .NET Framework 3.5 that improves communication between WCF and Windows Workflow Foundation (WF, or Workflow; see Workflow Services). I explained how you can expose a workflow as a WCF service and how you can call WCF services from a workflow. In the process you also learned how to configure two activities: ReceiveActivity and SendActivity. ReceiveActivity helps you implement a WCF service operation as part of a workflow, and SendActivity helps you to call a specific WCF service operation.

 

I left the discussion of securing Workflow Services for this month so I could spend time on the details. If you care about security and most of us do you ll want to know how you can secure calls into the Workflow Service through each ReceiveActivity, as well as how you can make secure calls to WCF services from a SendActivity. The remainder of this article will summarize what you can do, what the limitations are, and what workarounds are available.

 

ReceiveActivity Security Features

Workflow Services are typically initialized through a call to a ReceiveActivity which is an implementation of a WCF service operation. Calls that execute a ReceiveActivity can be secured in the following manner:

  • WCF configuration settings for the ReceiveActivity endpoint control the client credential type, authentication behavior, and authorization behavior.
  • Role-based permission demands can be set through ReceiveActivity properties.
  • Custom authorization code can be provided through ReceiveActivity properties.

 

Figure 1 illustrates the execution order of these security features. The WCF pipeline authenticates and authorizes credentials according to the associated service behavior. Before initializing the workflow instance, role-based permission settings for the ReceiveActivity are validated using traditional .NET Framework role-based permission demands. After the workflow is initialized, any custom code supplied to validate the caller is executed prior to executing the ReceiveActivity sequence.

 


Figure 1: ReceiveActivity and related security checkpoints.

 

Note: The workflow instance beginning with the ReceiveActivity sequence will be executed on a new thread, distinct from the initial WCF thread allocated to the request. This will become important when I discuss identities.

 

In the next sections I ll discuss each of these security features in more detail.

 

Authenticating and Authorizing Client Credentials

A ReceiveActivity is associated with a particular service contract and operation. The host process for a Workflow Service loads a service model configuration for the service type implementing that contract (so clients can send messages that will reach the implementing ReceiveActivity). In that sense, standard WCF configuration settings are used to secure calls to a Workflow Service endpoint.

 

Figure 2 shows an example of a WCF configuration that defines a Workflow Service with a single endpoint. The settings require a UserName credential, authentication using a custom password validator, and authorization using a custom authorization policy.

 

 

   

    behaviorConfiguration="serviceBehaviorUserName">

     

      binding="wsHttpContextBinding" bindingConfiguration="wsHttpUserName"

      contract="IWFService">

     

   

 

 

   

     

       

         

       

     

   

 

 

   

     

       

        

          customUserNamePasswordValidatorType="SequentialWFService.CustomPasswordValidator,

          SequentialWFService"/>

         

          x509FindType="FindBySubjectName"/>

       

       

         

           

         

       

     

   

 

Figure 2: Sample service model configuration for a Workflow Service.

 

Note: Obviously security features of WCF are quite extensive. I assume you have some familiarity with WCF security to follow along here. If not, see the Additional Resources sidebar.

 

A context-aware binding is required for Workflow Services, which means the service endpoint must use either BasicHttpContextBinding, WSHttpContextBinding, or NetTcpContextBinding. Collectively, these bindings support a number of different credential types that can be sent using transport headers (transport security) or in SOAP message headers (message security). Figure 3 lists the supported credential types for each of the context-aware bindings.

 

Binding

Transport Credentials

Message Credentials

BasicHttpContextBinding

None, Basic, Digest, NTML, Windows, Certificate

UserName, Certificate

WSHttpContextBinding

None, Basic, Digest, NTML, Windows, Certificate

None, Windows, UserName, Certificate, IssuedToken

NetTcpContextBinding

None, Windows, Certificate

None, Windows, UserName, Certificate, IssuedToken

Figure 3: Credential support for context-aware bindings.

 

As Figure 2 suggests, the binding configuration indicates the credential type for the service. The selected client credential influences the choice of authentication and authorization behaviors configured for the service. The service behavior in Figure 2 includes a section, which indicates the certificate to use for mutual authentication and encryption of incoming messages. The section indicates that a custom password validator will be used to authenticate the call (a common alternative is to use the ASP.NET membership provider). The section specifies a custom authorization policy that can be used to collect the roles of the authenticated caller and use them to authorize calls.

 

Every credential is associated with a default authentication mechanism that can be customized in the associated service behavior configuration. Authorization behaviors also can be customized for the expected credential type. Figure 4 lists some typical credential types with a summary of authentication and authorization settings that might be useful for workflow services.

 

Credential Type

Authentication

Authorization

Windows

Authenticate against the Windows domain.

Gather roles from the Windows domain.

UserName

Authenticate against ASP.NET database using the ASP.NET membership provider or, against a custom database using a custom password validator.

Gather roles from the ASP.NET database using the ASP.NET role provider. Or, gather roles and other claims from a custom database using a custom authorization policy.

Certificate

Authenticate against the certificate store.

Specify roles or additional claims using a custom authorization policy.

IssuedToken

Authenticate the signing certificate for the token. This is not always possible if the certificate is not known in advance, as in tokens issued via CardSpace personal cards.

Specify roles or additional claims using a custom authorization policy.

Figure 4: Typical credential types and associated authentication and authorization behaviors.

 

WCF provides an authentication behavior for each credential type that can be customized to meet application requirements. For example, the UserNameAuthenticationBehavior configured in Figure 2 does not use the default Windows domain authentication, but instead uses a custom password validator. Each credential type has different authentication options appropriate to the type.

 

Authorization behaviors for the service also can be customized. By default, the Windows domain is used to authorize requests which means that Windows roles are gathered for the supplied credential. The ServiceAuthorizationBehavior in Figure 2 specifies a custom authorization policy to be responsible for evaluating the identity of the caller and gathering roles or other relevant claims. In addition, this is where service credentials are supplied (as needed) to secure communications.

 

The point here is not to dissect every possible scenario as there are many but to provide insight into how WCF handles authentication and authorization before we discuss how Workflow Services are affected. As Figure 4 suggests, authentication behaviors should be configured according to the credential type and application requirements just like any other WCF service. Authorization behaviors for a Workflow Service should ensure these two things:

  • That a valid security principal is attached to the WCF thread prior to executing any role-based security checks.
  • That any information such as user roles or other claims describing the caller be added to the security context prior to initializing the workflow thread.

 

I ll discuss identities and authorization policies in the next few sections.

 

Threads, Identities, and ServiceSecurityContext

If you re familiar with .NET Framework security features, you know that every process runs under a specific Windows identity, and that every thread has a security principal (an IPrincipal object) that can be accessed via Thread.CurrentPrincipal. The process identity governs the Windows resources to which your code has access, and is often used in the authentication and authorization of calls made across processes. The security principal is employed in .NET Framework role-based security (as in permission demands).

 

Figure 5 illustrates how Workflow Services allocates these identities for incoming calls to a ReceiveActivity. When WCF authenticates the request, an identity is created (an IIdentity object) for the credential. Usually this identity becomes part of the security principal attached to the thread via the installed authorization policy. In addition, any claims associated with the identity are added to the security context (accessed through the AuthorizationContext property of the ServiceSecurityContext). Role-based security for Workflow Services depends on the Thread.CurrentPrincipal to be set for the WCF thread.

 


Figure 5: Workflow Services, process identities, and security principals.

 

When the Workflow thread is created, the security principal from the WCF thread is no longer accessible. Presumably, authentication has already taken place. Authorization may have already been completed through role-based permission demands. If not, to perform additional authorization the Workflow thread relies on the security context to include the necessary claim sets that describe the authenticated caller. The Workflow thread has an empty security principal (a GenericPrincipal type), making traditional role-based permission demands useless from the executing Workflow thread.

 

This is important. The only way to pass meaningful information about the authenticated caller to the Workflow thread is to supply the security context of the WCF thread with claims that will be useful to the Workflow thread. In the case of Windows, Certificate, and IssuedToken credentials, this is automatically handled in that Windows groups, Certificate claims, and IssuedToken (usually SAML token) claims are provided in a ClaimSet. If you want to add additional claims based on the authenticated caller s identity, a custom authorization policy must be provided, as suggested in Figure 4.

 

For the scenario in Figure 2, the UserNameAuthorizationPolicy adds roles for the authenticated users into a new ClaimSet, as shown in the IAuthorizationPolicy implementation in Figure 6. To simplify things, this example shows you how to hard-code the roles for the caller (obviously you d be looking up the roles based on the user name in a real implementation).

 

public class UserNameAuthorizationPolicy: IAuthorizationPolicy

{

 public bool Evaluate(EvaluationContext evaluationContext,

                      ref object state)

 {

            

   List identities = evaluationContext.Properties["Identities"]

      as List;

   IIdentity identity = identities[0];

   evaluationContext.Properties["Principal"] = new

      GenericPrincipal(identity, new string[] {"Administrators", "Users"});

   evaluationContext.AddClaimSet(this,new DefaultClaimSet(new Claim("Role",

      "Administrators", Rights.PossessProperty), new Claim("Role",

     "Users", Rights.PossessProperty)));

          

   return true;

 }

 // remaining implementation

}

Figure 6: A sample custom authorization policy for UserName credentials.

 

This ClaimSet then appears in the claim sets available to the Workflow thread.

 

The obvious limitation here is that the Workflow thread does not have access to the original security token, and thus cannot impersonate it or otherwise use it for calls to downstream WCF services. I wouldn t generally recommend impersonation for downstream service calls, so this is not an issue. As for access to the original security token, this would be an issue in federated security scenarios.

 

Note: Although there isn t an out-of-the-box context-aware binding to support federation, in theory, a custom binding can be created to support context. In this situation it can be an issue if the token is not available to the Workflow thread.

 

Configuring Role-based Security

Now that you understand the threading model and related identities, we can discuss how Workflow Services implement role-based security. To configure role-based security for ReceiveActivity, set values in the Permissions tab of the Choose Operation dialog box (see Figure 7), which is presented when you configure the ServiceOperationInfo property for the ReceiveActivity.

 


Figure 7: Configuring role-based security permissions for the ReceiveActivity.

 

You can specify a particular user name (not very useful) or a list of roles (only useful if you are doing role-based security and not claims-based security). The role-based check is executed using the Thread.CurrentPrincipal.IsInRole function, and thus expects there to be an appropriate security principal attached to the thread. In the case of Windows credentials, this is automatic. In the case of UserName, Certificate, or IssuedToken credentials, you must provide an appropriate authorization behavior and policy to create a security principal for the WCF thread. The ASP.NET role provider handles this for you; otherwise, you re on your own. Figure 6 shows how to create a security principal for the WCF thread using a custom authorization policy.

 

If the role-based check passes, the ReceiveActivity sequence is executed on a new thread. At this point custom validation code will be executed, if present, giving you another opportunity to validate the call before execution of the sequence.

 

OperationValidation

Each ReceiveActivity has an OperationValidation property (which is also exposed as an OperationValidation event) that can be used to provide custom validation code for the ReceiveActivity. Figure 7 illustrates a custom handler named ValidateCaller. This code is called after role-based security checks for the ReceiveActivity are completed. This code can only access the ServiceSecurityContext to validate claims because the security principal attached to the thread is no longer representative of the original caller.

 

Security and SendActivity

Outgoing calls from a Workflow Service to downstream WCF services must be secured according to downstream service requirements. Each SendActivity is associated with an endpoint configuration that is used to initialize a dynamically generated proxy used to call a service operation. The credentials required by this endpoint configuration must somehow be provided to the proxy during its initialization. Unlike traditional client applications that manually construct their proxies and set credentials in, Workflow Services require some creative techniques to achieve the same result.

 

Several options exist to initialize the dynamically generated proxy with the required credentials. Figure 8 lists common credential types, the default behavior of the dynamic proxy resulting from the endpoint configuration, and possible initialization techniques for the proxy.

 

Credential Type

Proxy Default

Proxy Initialization

Windows

Proxy is initialized with the host process identity.

Can provide alternate Windows credentials using an endpoint behavior.

UserName

Proxy is not initialized with a UserName credential.

Can provide a UserName credential at run time using an endpoint behavior.

Certificate

Proxy is not initialized with a Certificate credential.

Can provide a client certificate statically or at run time using an endpoint behavior.

IssuedToken

Proxy is not initialized with an IssuedToken credential.

Can provide an IssuedToken at run time using an endpoint behavior.

Figure 8: Typical credential types and associated proxy initialization behaviors.

 

If a Windows credential is required by the SendActivity endpoint configuration, the host process credential is supplied to the proxy. This is a desirable result if Workflow Service is a middle-tier service in a trusted subsystem model, where downstream services need not know about the original caller s identity. There is no way to impersonate the originating caller, but, as I said, this is not a deficit in most cases (impersonation is not usually a good idea). It is not likely that a different Windows credential is required in this scenario, but it is possible to set a different credential at run time using an endpoint behavior. I ll discuss this option alongside UserName credentials.

 

Certificate credentials are also typically part of a trusted subsystem model, where a specific client certificate is used to authenticate the middle-tier code and authorize its access. In this case, a hard-coded certificate is acceptable. For example, the following endpoint configuration requires a client certificate (according to the binding) and provides this certificate in a static endpoint behavior:

 

 

   

     

       storeLocation="CurrentUser" storeName="My"

      x509FindType="FindBySubjectName"/>

   

 

 

Like the Windows credential, a certificate can be provided at run time using endpoint behaviors. UserName credentials should be supplied at run time. This can be done in two ways:

  • Provide a custom ClientCredentials type to an endpoint behavior to hook the run time and supply credentials.
  • Create a custom endpoint behavior to set the credential at run time, and trigger this using a custom behavior element.

 

The latter of these two techniques is not particularly difficult, but it does require several points of WCF extensibility that are out of the scope of this article. Providing a custom ClientCredentials type can be done by supplying the type name to the endpoint behavior, as shown in Figure 9.

 

 

   

    binding="netTcpBinding" bindingConfiguration="netTcpUserName"

     behaviorConfiguration="UserNameClientBehavior"

    contract="UserNameService.IAuthenticationService" name="UserNameService">

     

       

     

   

 

 

   

     

       

         

       

     

   

 

 

   

     

       

     

   

 

 

Figure 9: Client endpoint configuration with custom ClientCredentials type.

 

The implementation of the ClientCredentials type need only provide the UserName credentials in the ApplyClientBehavior override, as shown in Figure 10 (hard-coded for simplicity).

 

public class CustomClientCredentials: ClientCredentials

{

 public CustomClientCredentials(): base()

 {}

 public CustomClientCredentials(ClientCredentials credentials):

                                base(credentials)

 {}

 protected override ClientCredentials CloneCore()

 {

   return new CustomClientCredentials(this);

 }

 public override void ApplyClientBehavior(ServiceEndpoint

    serviceEndpoint, System.ServiceModel.Dispatcher.ClientRuntime behavior)

 {

   this.UserName.UserName = "username";

   this.UserName.Password = "password";

   base.ApplyClientBehavior(serviceEndpoint, behavior);

 }

}

Figure 10: A simple implementation of the ClientCredentials type.

 

In a similar fashion, a custom Windows, Certificate, or IssuedToken credential also can be supplied although it is not likely that an IssuedToken would be handled this way.

 

IssuedToken scenarios typically imply federation. A few things get in the way of federation scenarios and Workflow Services:

  • Assuming the ReceiveActivity authenticated and authorized the initial federated token issued by a Security Token Service (STS), this token is no longer available to the Workflow Service for downstream service calls. Normally the initial token would be used to request a new token for downstream services.
  • To request a new token to call downstream services, it would be much easier to create a custom activity that wraps a traditional WCF proxy that will handle calling the STS and initializing the IssuedToken automatically.

 

Conclusion

To properly secure Workflow Services it is important to understand how they differ from traditional WCF services. Specifically, understanding the threading model and how to pass information to the Workflow thread, and understanding how the dynamic proxy can be initialized according to downstream service requirements. In a trusted subsystem model, it should not be an issue that the credentials used to authenticate to a ReceiveActivity aren t available to the Workflow thread, and that the dynamic proxy uses the host process identity. If further customization is required, some tips have been provided in this article; you can explore more of these details in the accompanying code.

 

Download the samples for this article at http://www.dasblonde.net/downloads/asppronov08.zip.

 

Michele Leroux Bustamante is Chief Architect of IDesign Inc., Microsoft Regional Director for San Diego, and Microsoft MVP for Connected Systems. At IDesign Michele provides training, mentoring, and high-end architecture consulting services focusing on Web services, scalable and secure architecture design for .NET, federated security scenarios, Web services, interoperability, and globalization architecture. She is a member of the International .NET Speakers Association (INETA), a frequent conference presenter, conference chair for SD West, and is frequently published in several major technology journals. Michele also is on the board of directors for IASA (International Association of Software Architects), and a Program Advisor to UCSD Extension. Her latest book is Learning WCF (O Reilly 2007); visit her book blog at http://www.thatindigogirl.com. Reach her at mailto:[email protected], or visit http://www.idesign.net and her main blog at http://www.dasblonde.net.

 

Additional Resources

 

 

 

Hide comments

Comments

  • Allowed HTML tags: <em> <strong> <blockquote> <br> <p>

Plain text

  • No HTML tags allowed.
  • Web page addresses and e-mail addresses turn into links automatically.
  • Lines and paragraphs break automatically.
Publish