Click here to Skip to main content
15,886,199 members
Articles / Web Development / ASP.NET
Article

Username logging for ASP.NET & ASP using an ISAPI Filter

Rate me:
Please Sign up or sign in to vote.
4.71/5 (15 votes)
24 May 20037 min read 137.8K   1.9K   33   18
Provides for IIS logging of usernames to the IIS logs for sites that rely on ASP & ASP.NET.

Introduction

This ISAPI filter provides a simple means of logging user names within the IIS logging mechanism of sites, that implement authentication schemes entirely within ASP.NET or ASP, or any framework that is not integrated with the ISAPI filter system.

This includes ASP.NET authentication methods Forms & Passport and any home-grown code that provides username authentication. This doesn't apply to authentication methods that use an ISAPI filter -- such as those within MS Site Server, AuthentiX and similar implementations.

Background

Authentication of users on web sites, or just providing a means of identifying the user to the site is a common need. This allows the site to provide authorization over content access, profiling of usage, targeted delivery of content along with a host of other purposes.

Additionally, use of ad-hoc tools to analyze the Web server logs (IIS logs) can also be useful. Site Server 3.0 from Microsoft provided an ad-hoc tool that analyzed the IIS logs. It created rich content in HTML format that presented information about site usage, path through the site, how long users spent on pages, HTTP error responses, etc. Web Trends is another tool that provides similar ad-hoc analysis.

While these tools are useful, they are even more useful when specific user information is available. They can provide specific user access information as users make their way through your site.

Currently, ASP.NET (and ASP) doesn't provide this link to the IIS logs, other than providing the basic logging information that IIS itself traps, prior to forwarding requests onto the ASP.NET worker process or the ASP ISAPI extension.

This ISAPI filter provides this link through a simplistic means. By use of a cookie based mechanism, the IsapiLogger filter will examine user requests looking for the specific cookie name (controlled by the registry) and insert the associated cookie value as the username into the IIS log.

The only requirement is modification of your implementation to support the creation of the cookie at authentication time and if desired, clearing the cookie's value upon a "logoff" -- if your site supports logoff. For the logoff, it's not required to clear the cookie. Since the cookie is just a tag indicating the actual user, it by itself doesn't represent an authenticated user. This still has to be part of the underlying application. However, some sites may want to "track" a user's activity once they logoff.

Using the code

The code was originally developed using VC++ 6.0 up through SP5; it will also build under VS.NET (which is what the zip file contains). To utilize the code, simply create the registry entry (in the attached zipped files) and add the DLL as an ISAPI filter to the web site.

Registry entry

test
Windows Registry Editor Version 5.00
[HKEY_LOCAL_MACHINE\SOFTWARE\IsapiLogger]
"CookieName"="userid"

Filter Installation

Adding ISAPI Filter

Application changes / requirements

Once the ISAPI filter and registry settings are present, modify your ASP.NET or ASP application (or any implementation that can create cookies) to create the cookie upon logon.

For ASP.NET, I've created a page for testing, that allows setting and clearing the cookie, which is in the attached zip file.

Note that, clearing the cookie is done by just setting the value to an empty string. Since the browser ultimately controls what the actual cookies are with each request, you cannot actually "clear" a cookie. When the browser exists, the cookie will no longer be available for future sessions. The following two methods do the setting & clearing respectively.

C#
private void btnSetCookie_Click(object sender, System.EventArgs e)
{
    HttpCookie cookie = new HttpCookie(txtCookiename.Text, 
                                          txtUsername.Text);
    Response.Cookies.Add(cookie);
}
private void btnClearCookie_Click(object sender, System.EventArgs e)
{
    HttpCookie cookie = new HttpCookie(txtCookiename.Text, null);
    Response.Cookies.Add(cookie);
}

Filter project creation

The actual filter implementation is quite easy. To create the filter from scratch, open up VS.NET (which is what the following screen shots are from) and choose MFC ISAPI Extension DLL.

Creation of MFC ISAPI Filter Step 1

Select ISAPI project type

Enable the checkbox for Generate a filter object and be sure to disable the checkbox for Generate a server extension object. Also, I chose to use MFC in a shared DLL. You can choose that static mode also, however, a preferred route is to disable the link to MFC altogether and prevent it from loading at runtime. Since this is a lightweight filter, there's no need to have MFC loaded. I didn't do that with this project as I wanted to keep the article short and just focus on the filter creation and functionality.

Creation of MFC ISAPI Filter Step 2

Choose notifications

This filter is only interested in Server Log Writes. Note that the checkboxes for both secured and non-secured port sessions are also checked. My preference is to deselect both, but the wizard forces selection of at least one of them. This can be later disabled in the code. By default, the ISAPI filter, if it does not register for either, it will be notified for both types of sessions. Having it explicit may be preferred by some. In the end, either leaving both unchecked (if it was possible) or having them checked would result in both types of notifications being forwarded to the filter.

Creation of MFC ISAPI Filter Step 3

Implementation

For the most part, the code is un-interesting. The basic processing is first to implement the required GetFilterVersion for all ISAPI filters. Here, the code generated by the wizard is sufficient. The only addition is the LoadFromRegistry call.

There is also a GetUserName helper method along with the implementation of the OnLog virtual provided by the MFC ISAPI filter based class.

LoadFromRegistry

This simply just looks up the CookieName as provided by the HKEY_LOCAL_MACHINE\SOFTWARE\IsapiLogger section. The actual setting can be changed by modifying the IsapiLogger.h file.

void CIsapiLoggerFilter::LoadFromRegistry()
{
   HKEY hkey= NULL;
   DWORD datasize;
   RegOpenKeyEx(HKEY_LOCAL_MACHINE, registryTree, 0, KEY_READ, &hkey);
   datasize= sizeof(sm_cookie);
   //get the setting from the registry or default to "username"
   if( RegQueryValueExA(hkey, 
                  "CookieName", 
                  NULL, 
                  NULL, 
                  (LPBYTE)&sm_cookie, 
                  &datasize) != ERROR_SUCCESS || *sm_cookie == '\0')
       strcpy(sm_cookie, "username");
   


   sm_len_cookie = strlen(sm_cookie);
   RegCloseKey(hkey);
}

GetUserName

This call gets the buffer that represents the entire cookie collection from the request, via the notification event and loops through the cookie collection. It will find the first occurrence of the cookie key name and return either the value or a '-'. The dash represents an anonymous user and is required by the log call.

char * CIsapiLoggerFilter::getUserName(char buffer[])
{
   //cookies are separated by ; values by  =  then ignore spaces
   char seps[] = ";= "; 
   char *token;
   token = strtok( buffer, seps );
   while( token != NULL ){
      /* While there are tokens in "buffer" */
      if (!strcmp(token, sm_cookie)){
         token = strtok( NULL, seps );
         return token;
      }
   token = strtok( NULL, seps );
   }
   return "-\0"; //this is the Anonymous user in the logfiles a "dash"
}

OnLog

The OnLog method provides the required implementation from the base class. It simply pulls out the HTTP_COOKIE member of the pCtxt using the member method GetServerVariable as provided by the base MFC implementation. Once it has the cookie collection, it calls into getUserName which returns either the user string associated with the cookie or the anonymous indicator.

DWORD CIsapiLoggerFilter::OnLog(CHttpFilterContext* pCtxt, 
                                       PHTTP_FILTER_LOG pLog) 
{
   char szBuffer[bufferSize] = "\0"; //NULL;
   DWORD dwSize = bufferSize;
   pCtxt->GetServerVariable("HTTP_COOKIE", szBuffer, &dwSize);
   if (strlen (szBuffer) > 0){
      pLog->pszClientUserName = getUserName(szBuffer);
   }
   return SF_STATUS_REQ_NEXT_NOTIFICATION;
}

Conclusion / Points of interest

One thing I did learn during the original creation of the filter is not to discount the site sponsor's desire to have relevant information regarding user activity on the site. The original requirements didn't specify the need for ad-hoc reporting, even though it was asked if needed. This wasn't added until the build had progressed well through implementation. By using this simplistic method, which didn't require modification to the home-grown authentication / authorization code already implemented, we were able to quickly deploy Web Trends to support the ad-hoc analysis as requested by the project sponsor.

Since the original site requirements didn't specify these needs, we had not chosen Site Server 3.0 for its ISAPI filter implementation (which BTW was one of the strongest features) or other products such as AuthentiX. The original needs for authentication was simplistic and we already had a session management capability built against SQL server.

Another interesting note it this issue and this implementation applies to ASP.NET. When I first heard of .NET over 2 years ago, and the inclusion of the "Filter Like" built-in authentication / authorization framework, I was looking forward to basing future sites on it. However, after further investigation of how the ASP.NET implementation was done, it became clear that the ASP.NET authentication mode that I was most interested in -- Forms authentication -- was not wired up to the IIS logging. This to me is a disappointment. I'm looking forward to .NET server with IIS 6 and the full integration of ASP.NET, along with the numerous other improvements, to address this seemingly trivial issue.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Web Developer
United States United States
I have over 15 years of experience in the Application Development. Unfortunately, I still remember punch cards from College and VisiCalc on the Apple II.

My recent experience (about 6 years) covers the 2 main camps in distributed computing: J2EE based and COM[+] / .NET.

Lately, it's been deep .NET, C#, ASP.NET and the rest of the .NET Framework.

I've been working on Internet related technologies since 1993, initially writing Perl scripts under the original NCSA Http server.

Comments and Discussions

 
QuestionUpdated for IIS7 64bit? Pin
Owen3712-Apr-11 8:15
Owen3712-Apr-11 8:15 
QuestionWill not load on IIS 6.0 Pin
turtleship20067-Nov-06 4:13
turtleship20067-Nov-06 4:13 
Generaliisutil error Pin
L Norton17-Apr-06 1:55
L Norton17-Apr-06 1:55 
QuestionEvent Log Security Exception Pin
Aanchal Naidu16-Dec-05 14:35
Aanchal Naidu16-Dec-05 14:35 
AnswerRe: Event Log Security Exception Pin
Shawn Cicoria16-Dec-05 23:06
Shawn Cicoria16-Dec-05 23:06 
QuestionOff by one error in cookie buffer termination? Pin
Ty Durden15-Nov-05 18:17
Ty Durden15-Nov-05 18:17 
AnswerRe: Off by one error in cookie buffer termination? Pin
Shawn Cicoria16-Nov-05 2:22
Shawn Cicoria16-Nov-05 2:22 
QuestionIIS AUTHENTICATION? Pin
SherKar27-Sep-04 22:24
SherKar27-Sep-04 22:24 
GeneralProblems with the filter Pin
smcintyr_1119-Jan-04 12:10
smcintyr_1119-Jan-04 12:10 
GeneralRe: Problems with the filter Pin
Shawn Cicoria19-Jan-04 13:11
Shawn Cicoria19-Jan-04 13:11 
GeneralRe: Problems with the filter Pin
Ted Broyles20-Apr-04 9:13
Ted Broyles20-Apr-04 9:13 
GeneralRe: Problems with the filter Pin
Shawn Cicoria20-Apr-04 9:25
Shawn Cicoria20-Apr-04 9:25 
QuestionConvert from C# to VB.NET possible? Pin
ruseno1-Sep-03 5:30
ruseno1-Sep-03 5:30 
AnswerRe: Convert from C# to VB.NET possible? Pin
Shawn Cicoria1-Sep-03 7:09
Shawn Cicoria1-Sep-03 7:09 
GeneralRe: Convert from C# to VB.NET possible? Pin
ruseno3-Sep-03 9:51
ruseno3-Sep-03 9:51 
Generalgood Pin
Jesus Oliva17-Dec-02 3:01
Jesus Oliva17-Dec-02 3:01 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.