How to: Lock Down Endpoints in the Enterprise

Large enterprises often require that applications are developed in compliance with enterprise security policies. The following topic discusses how to develop and install a client endpoint validator that can be used to validate all Windows Communication Foundation (WCF) client applications installed on computers.

In this case, the validator is a client validator because this endpoint behavior is added to the client <commonBehaviors> section in the machine.config file. WCF loads common endpoint behaviors only for client applications and loads common service behaviors only for service applications. To install this same validator for service applications, the validator must be a service behavior. For more information, see the <commonBehaviors> section.

Aa751866.Important(en-us,VS.100).gif Note:
Service or endpoint behaviors not marked with the AllowPartiallyTrustedCallersAttribute attribute (APTCA) that are added to the <commonBehaviors> section of a configuration file are not run when the application runs in a partial trust environment, and no exception is thrown when this occurs. To enforce the running of common behaviors such as validators, you must either:

-- Mark your common behavior with the AllowPartiallyTrustedCallersAttribute attribute so that it can run when deployed as a Partial Trust application. Note that a registry entry can be set on the computer to prevent APTCA-marked assemblies from running..

-- Ensure that if the application is deployed as a fully-trusted application that users cannot modify the code-access security settings to run the application in a Partial Trust environment. If they can do so, the custom validator does not run and no exception is thrown. For one way to ensure this, see the levelfinal option using Code Access Security Policy Tool (Caspol.exe).

For more information, see Partial Trust Best Practices and Supported Deployment Scenarios.

To create the endpoint validator

  1. Create an IEndpointBehavior with the desired validation steps in the Validate method. The following code provides an example. (The InternetClientValidatorBehavior is taken from the Security Validation sample.)

    public class InternetClientValidatorBehavior : IEndpointBehavior
    {
        public void AddBindingParameters(ServiceEndpoint serviceEndpoint, System.ServiceModel.Channels.BindingParameterCollection bindingParameters) { }
        public void ApplyClientBehavior(ServiceEndpoint serviceEndpoint, System.ServiceModel.Dispatcher.ClientRuntime behavior) { }
        public void ApplyDispatchBehavior(ServiceEndpoint serviceEndpoint, System.ServiceModel.Dispatcher.EndpointDispatcher endpointDispatcher) { }
    
        public void Validate(ServiceEndpoint endpoint)
        {
            BindingElementCollection elements = endpoint.Binding.CreateBindingElements();
    
            if (EndpointIsDual(endpoint, elements))
                throw new InvalidOperationException("InternetClientValidator: endpoint uses 'dual' mode. This mode is disallowed for use with untrusted services.");
    
            if (EndpointAllowsNtlm(endpoint, elements))
                throw new InvalidOperationException("InternetClientValidator: endpoint allows NTLM. This mode is disallowed for use with untrusted services.");
    
            if (EndpointAllowsTransactionFlow(endpoint, elements))
                throw new InvalidOperationException("InternetClientValidator: endpoint flows transaction ids. This mode is disallowed for use with untrusted services.");
        }
    
  2. Create new BehaviorExtensionElement that registers the endpoint validator created in step 1. The following code example shows this. (The original code for this example is in the Security Validation sample.)

    public class InternetClientValidatorElement : BehaviorExtensionElement
    {
        public override Type BehaviorType
        {
            get { return typeof(InternetClientValidatorBehavior); }
        }
    
        protected override object CreateBehavior()
        {
            return new InternetClientValidatorBehavior();
        }
    }
    
  3. Make sure the compiled assembly is signed with a strong name. For details, see the Strong Name Tool (Sn.exe) and the compiler commands for your language.

To install the validator into the target computer

  1. Install the endpoint validator using the appropriate mechanism. In an enterprise, this can be using Group Policy and Systems Management Server (SMS).

  2. Install the strongly-named assembly into the global assembly cache using the Global Assembly Cache Tool (Gacutil.exe).

  3. Use the System.Configuration namespace types to:

    1. Add the extension to the <behaviorExtensions> section using a fully-qualified type name and lock the element.

      // Register our validator configuration element.
      ExtensionsSection extensions
        = machine.GetSection(@"system.serviceModel/extensions") as ExtensionsSection;
      if (extensions == null)
        throw new Exception("not extensions section.");
      ExtensionElement validator 
        = new ExtensionElement(
          "internetClientValidator", 
          "Microsoft.ServiceModel.Samples.InternetClientValidatorElement, InternetClientValidator, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"
        );
      validator.LockItem = true;
      if (extensions.BehaviorExtensions.IndexOf(validator) < 0)
        extensions.BehaviorExtensions.Add(validator);
      
    2. Add the behavior element to the EndpointBehaviors property of the <commonBehaviors> section and lock the element. (To install the validator on the service, the validator must be an IServiceBehavior and added to the ServiceBehaviors property.) The following code example shows the proper configuration after steps a. and b., with the sole exception that there is no strong name.

      // Add a new section for our validator and lock it down.
      // Behaviors for client applications must be endpoint behaviors.
      // Behaviors for service applications must be service behaviors.
      CommonBehaviorsSection commonBehaviors
        = machine.GetSection(@"system.serviceModel/commonBehaviors") as CommonBehaviorsSection;
      InternetClientValidatorElement internetValidator = new InternetClientValidatorElement();
      internetValidator.LockItem = true;
      commonBehaviors.EndpointBehaviors.Add(internetValidator);
      
    3. Save the machine.config file. The following code example performs all the tasks in step 3 but saves a copy of the modified machine.config file locally.

      // Write to disk.
      machine.SaveAs("newMachine.config");
      
      // Write our new information.
      SectionInformation cBInfo = commonBehaviors.SectionInformation;
      Console.WriteLine(cBInfo.GetRawXml());
      Console.WriteLine(extensions.SectionInformation.GetRawXml());
      Console.Read();
      

Example

The following code example shows how to add a common behavior to the machine.config file and save a copy to the disk. The InternetClientValidatorBehavior is taken from the Security Validation sample.

Configuration machine = ConfigurationManager.OpenMachineConfiguration();
// Register our validator configuration element.
ExtensionsSection extensions
  = machine.GetSection(@"system.serviceModel/extensions") as ExtensionsSection;
if (extensions == null)
  throw new Exception("not extensions section.");
ExtensionElement validator 
  = new ExtensionElement(
    "internetClientValidator", 
    "Microsoft.ServiceModel.Samples.InternetClientValidatorElement, InternetClientValidator, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"
  );
validator.LockItem = true;
if (extensions.BehaviorExtensions.IndexOf(validator) < 0)
  extensions.BehaviorExtensions.Add(validator);
 
// Add a new section for our validator and lock it down.
// Behaviors for client applications must be endpoint behaviors.
// Behaviors for service applications must be service behaviors.
CommonBehaviorsSection commonBehaviors
  = machine.GetSection(@"system.serviceModel/commonBehaviors") as CommonBehaviorsSection;
InternetClientValidatorElement internetValidator = new InternetClientValidatorElement();
internetValidator.LockItem = true;
commonBehaviors.EndpointBehaviors.Add(internetValidator);
// Write to disk.
machine.SaveAs("newMachine.config");

// Write our new information.
SectionInformation cBInfo = commonBehaviors.SectionInformation;
Console.WriteLine(cBInfo.GetRawXml());
Console.WriteLine(extensions.SectionInformation.GetRawXml());
Console.Read();

Security

You may also want to encrypt the configuration file elements. For more information, see the See Also section.

See Also

Other Resources

Encrypting configuration file elements using DPAPI
Encrypting configuration file elements using RSA