ASP.NET Extensibility Code and Security Context

Whenever you write infrastructure code in ASP.NET, e.g. a module or a provider, that accesses external resources (files, databases etc) you heavily rely on the security context of the current request. And since modules or providers are made for re-use, you should be prepared for all eventualities.

By default, external resource access is done using the identity of the worker process (e.g. NETWORK SERVICE). But there are several ways to change the security context via configuration, e.g.

<identity impersonate=”true” />

This changes the security context of the current requests to the client’s identity.

<identity impersonate=”true” username=”xxx” password=”xxx” />

This changes the security context of the current requests to a fixed identity.

So whenever you access a resource, you should check how configuration influences your security context. In the case where you don’t impersonate, you use the process identity, which may be exactly what you want. Application impersonation is basically used to change the security context of an application as a whole in situations where no IIS6 application pools can be used (too many different app pools or IIS5). So you should respect this setting.
However – you might get in trouble when client impersonation is configured. This means that every single client has to have access to the resource you are accessing. This may be what you want, but not necessarily.

A simple example:
Assume you are writing a logging module or a Health Monitoring provider to log to a database. The security context is predictable if you are using the process or application identity. But if client impersonation is enabled (and you are using integrated authentication with SQL Server), the database connection is made using the client credentials. In most cases you don’t want to give every single user of your application write or execute access to the stored procs or tables in your logging database. As a side note – if the accessed resource is non-local you may also run into all kinds of delegation related problems. The same applies to all kinds of Windows kernel objects (files, registry, performance counters etc…).

In this case you should programmatically revert to the process identity during resource access. But you have to be careful to change the security context back to whatever it was before, otherwise you will modify the context for the rest of the request which may lead to other unpredictable results.

So if this is a concern to you (and I know how many people out there are writing providers), you should take actions according to your security configuration, or use a configuration attribute to configure your behavior (like session state does with useHostingIdentity=”true/false”).

You can check if you are currently impersonating with this code:

if (WindowsIdentity.GetCurrent(true) != null)

  // impersonation is enabled

There are two approaches to figure out if you are currently impersonating the client or a fixed identity. One involves checking the configuration:

IdentitySection id = (IdentitySection)

  WebConfigurationManager.GetSection(“system.web/identity”);

 

if (id.Impersonate == true &&

   !string.IsNullOrEmpty(id.UserName) &&     

   !string.IsNullOrEmpty(id.Password))

{ }

The downside of this approach is that you need ConfigurationPermission which is not available in medium trust. A more lightweight way is to compare the current security context to the IIS’ authentication outcome. If they match, you are running in the security context of the client. There is one edge case though. When you authenticate with IIS using the same credentials as the ones specified in the <identity> element, this comparison gives you the wrong result. But that’s rather rare.

if (!string.Equals(

   context.Request.LogonUserIdentity.Name,

   WindowsIdentity.GetCurrent().Name,  

   StringComparison.OrdinalIgnoreCase))

{ }

To wrap it up – your logging code could look like this:

private void log(HttpContext ctx)

{

  SecurityContextState state =

    SecurityHelper.GetSecurityContextState();

   

  WindowsImpersonationContext wic = null;

  try

  {

    // check if client impersonation is enabled

    // and if yes – revert to process identity

    if (state == SecurityContextState.ClientImpersonation)

      wic = WindowsIdentity.Impersonate(IntPtr.Zero);

 

    // access database

  }

  catch

  {

    if (wic != null)

    {

      // undo revert to process identity

      wic.Undo();

      wic = null;

    }

      throw;

  }

  finally

  {

    if (wic != null)

      wic.Undo();

  }

}

And the helper class:

public class SecurityHelper

{

  public static SecurityContextState GetSecurityContextState()

  {

    HttpContext context = HttpContext.Current;

 

    if (WindowsIdentity.GetCurrent(true) != null)

    {

      if (!string.Equals(

         context.Request.LogonUserIdentity.Name,

         WindowsIdentity.GetCurrent().Name,

         StringComparison.OrdinalIgnoreCase))

        return SecurityContextState.ApplicationImpersonation;

      else

        return SecurityContextState.ClientImpersonation;

    }

    else

      return SecurityContextState.HostingIdentity; 

  }

}

 

public enum SecurityContextState

{

    HostingIdentity,

    ClientImpersonation,

    ApplicationImpersonation

}

 

This entry was posted in Uncategorized. Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s