Security Auditing

I will end this chapter by presenting a useful feature WCF supports called security audits. As its name implies, a security audit is a logbook of the security-related events in your services. WCF can log authentication and authorization attempts, their times and locations, and the calling clients’ identities. The class ServiceSecurityAuditBehavior governs auditing; it is listed in Example 10-30 along with its supporting enumerations.

Example 10-30. The ServiceSecurityAuditBehavior class

public enum AuditLogLocation
{
   Default, //Decided by the operating system
   Application,
   Security
}
public enum AuditLevel
{
   None,
   Success,
   Failure,
   SuccessOrFailure
}
public sealed class ServiceSecurityAuditBehavior : IServiceBehavior
{
   public AuditLogLocation AuditLogLocation
   {get;set;}
   public AuditLevel MessageAuthenticationAuditLevel
   {get;set;}
   public AuditLevel ServiceAuthorizationAuditLevel
   {get;set;}
   //More members
}

ServiceSecurityAuditBehavior is a service behavior. The AuditLogLocation property specifies where to store the log entries: in the application log or in the security log, both of which are in the event log on the host computer. The MessageAuthenticationAuditLevel property governs the authentication audit verbosity. Its default value is AuditLevel.None. For performance’s sake, you may want to audit only failures. For diagnostic purposes, you can also audit successful authentications. Similarly, you use the ServiceAuthorizationAuditLevel property to control authorization audit verbosity. It is also disabled by default.

Configuring Security Audits

The typical way of enabling a security audit is in the host config file, by adding a custom behavior section and referencing it in the service declaration, as shown in Example 10-31.

Example 10-31. Configuring a security audit administratively

<system.serviceModel>
   <services>
      <service name = "MyService" behaviorConfiguration = "MySecurityAudit">
         ...
      </service>
   </services>
   <behaviors>
      <serviceBehaviors>
         <behavior name = "MySecurityAudit">
            <serviceSecurityAudit
               auditLogLocation = "Default"
               serviceAuthorizationAuditLevel  = "SuccessOrFailure"
               messageAuthenticationAuditLevel = "SuccessOrFailure"
            />
         </behavior>
      </serviceBehaviors>
   </behaviors>
</system.serviceModel>

You can also configure security auditing programmatically, by adding the behavior to the host at runtime before opening it. As you do when adding other behaviors programmatically, you can check that the host does not already have an audit behavior to avoid overriding the config file, as shown in Example 10-32.

Example 10-32. Enabling a security audit programmatically

ServiceHost host = new ServiceHost(typeof(MyService));

ServiceSecurityAuditBehavior securityAudit =
                   host.Description.Behaviors.Find<ServiceSecurityAuditBehavior>();
if(securityAudit == null)
{
   securityAudit = new ServiceSecurityAuditBehavior();

   securityAudit.MessageAuthenticationAuditLevel = AuditLevel.SuccessOrFailure;
   securityAudit.ServiceAuthorizationAuditLevel = AuditLevel.SuccessOrFailure;
   host.Description.Behaviors.Add(securityAudit);
}
host.Open();

You can streamline the code in Example 10-32 by adding the SecurityAuditEnabled Boolean property to ServiceHost<T>:

public class ServiceHost<T> : ServiceHost
{
   public bool SecurityAuditEnabled
   {get;set;}
   //More members
}

Using ServiceHost<T>, Example 10-32 is reduced to:

ServiceHost<MyService> host = new ServiceHost<MyService>();
host.SecurityAuditEnabled = true;
host.Open();

Example 10-33 shows the implementation of the SecurityAuditEnabled property.

Example 10-33. Implementing the SecurityAuditEnabled property

public class ServiceHost<T> : ServiceHost
{
   public bool SecurityAuditEnabled
   {
      get
      {
         ServiceSecurityAuditBehavior securityAudit =
                        Description.Behaviors.Find<ServiceSecurityAuditBehavior>();
         if(securityAudit != null)
         {
            return securityAudit.MessageAuthenticationAuditLevel ==
                                                        AuditLevel.SuccessOrFailure
                   &&
                   securityAudit.ServiceAuthorizationAuditLevel ==
                                                       AuditLevel.SuccessOrFailure;
         }
         else
         {
            return false;
         }
      }
      set
      {
         if(State == CommunicationState.Opened)
         {
            throw new InvalidOperationException("Host is already opened");
         }
         ServiceSecurityAuditBehavior securityAudit =
                        Description.Behaviors.Find<ServiceSecurityAuditBehavior>();
         if(securityAudit == null && value == true)
         {
            securityAudit = new ServiceSecurityAuditBehavior();
            securityAudit.MessageAuthenticationAuditLevel =
                                                       AuditLevel.SuccessOrFailure;
            securityAudit.ServiceAuthorizationAuditLevel =
                                                       AuditLevel.SuccessOrFailure;
            Description.Behaviors.Add(securityAudit);
         }
      }
   }
   //More members
}

In the get accessor, the SecurityAuditEnabled property accesses the description of the service and looks for an instance of ServiceSecurityAuditBehavior. If one is found, and if both the authentication and the authorization audits are set to AuditLevel.SuccessOrFailure, SecurityAuditEnabled returns true; otherwise, it returns false. In the set accessor, the property enables the security audit only if the description does not contain a previous value (because the config file does not contain the audit behavior). If no prior behavior is found, SecurityAuditEnabled sets both the authentication and authorization audits to AuditLevel.SuccessOrFailure.

Declarative Security Auditing

You can also write an attribute that surfaces the security audit options at the service level. I chose to add that support in the form of a single Boolean property of the SecurityBehavior attribute called SecurityAuditEnabled:

[AttributeUsage(AttributeTargets.Class)]
public class SecurityBehaviorAttribute : Attribute,IServiceBehavior
{
   public bool SecurityAuditEnabled
   {get;set;}
   //More members
}

The default of SecurityAuditEnabled is false (i.e., no security audit). Using this property complements the rest of the declarative security model. For example:

[SecurityBehavior(ServiceSecurity.Internet,UseAspNetProviders = true,
                  SecurityAuditEnabled = true)]
class MyService : IMyContract
{...}

Example 10-34 shows how that support was added to the SecurityBehavior attribute.

Example 10-34. Implementing a declarative security audit

[AttributeUsage(AttributeTargets.Class)]
public class SecurityBehaviorAttribute : Attribute,IServiceBehavior
{
   public bool SecurityAuditEnabled
   {get;set;}

   void IServiceBehavior.Validate(ServiceDescription description,
                                  ServiceHostBase serviceHostBase)
   {
      if(SecurityAuditEnabled)
      {
         ServiceSecurityAuditBehavior securityAudit = serviceHostBase.Description.
                                   Behaviors.Find<ServiceSecurityAuditBehavior>();
         if(securityAudit == null)
         {
            securityAudit = new ServiceSecurityAuditBehavior();
            securityAudit.AuditLogLocation = AuditLogLocation.Application;
            securityAudit.MessageAuthenticationAuditLevel =
                                                      AuditLevel.SuccessOrFailure;
            securityAudit.ServiceAuthorizationAuditLevel =
                                                      AuditLevel.SuccessOrFailure;
            serviceHostBase.Description.Behaviors.Add(securityAudit);
         }
         //Rest same as Example 10-20
      }
   }
   //Rest of the implementation
}

The Validate() method of IServiceBehavior enables auditing using the same verbosity level as ServiceHost<T>, again avoiding overriding the config file.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.227.111.33