ISAPI Filter for Logging using MFC






4.50/5 (4 votes)
Aug 9, 2007
9 min read

52580

590
Implementing ISAPI Filter in IIS 5.x/6.x for logging HTTP/HTTPS request & response data

Introduction
Having worked with ISAPI Filter (Internet Server Application Programming Interface Filter) for more than a couple of months, I would like to share my experience through the development of such filters from scratch. I believe this would be a good startup tutorial for beginners who wish to work in ISAPI filters.
I would like to walkthrough the path of developing an ISAPI filter for logging all the request and response data to/from IIS. I would be using the MFC framework to get the skeleton of an ISAPI filter, which has the advantage of tight coupling combined with the IIS, as well as an ease of programming compared to the standard STL. I would be focusing on IIS 5.x and IIS 6.x for the development of ISAPI filter. I would also be writing in my next article how to develop such filters using the .NET framework with the help of HTTPModule. Though HTTPModule can be conceptually used to simulate the ISAPI filter functionality, it can only be plugged into a .NET based web applications. Other request/response to non- .NET based web applications (e.g. ASP, CFM, HTML pages) will not be incepted using the HTTPModule. But I guess this would be possible in the future version of IIS (IIS 7.0).
Ok, here we go…
First, let's see some basic concepts of ISAPI Filters.
Concepts
An ISAPI filter is a replaceable dynamic-link library (DLL) that the server calls on every HTTP(S) (Hypertext Transfer Protocol) request/response. When the filter is loaded, it tells the server what sort of notifications it is interested in. After that, whenever the selected events occur in IIS, the filter is called and given the opportunity to process that event. Some of the common functions/situation for which ISAPI filters used are,
- Custom authentication schemes
- Compression
- Encryption
- Logging
- Traffic analysis or other request analysis (e.g. Looking for requests to "..\..\etc\password")
Multiple filters can be installed on a single web site in IIS or globally to all web sites present in that IIS. The notification order is based on the priority specified by the filter and the load order in the registry for any ties. Note that once a filter has expressed interest in a request/response, it will receive that data regardless of whether the request/response is for a file, a CGI (Common Gateway Interface, a common HTTP server extension) application, or an ISAPI application.
ISAPI filters can be used to enhance the Microsoft Internet Information Server with custom features such as enhanced logging of HTTP requests/responses, custom encryption, and compression schemes, or new authentication methods. The filter applications sit between the network connection to the clients and the HTTP server. Depending on the options that the filter application chooses, it can act on several server actions, including reading raw data from the client, processing the headers, communications over a secure port (PCT - Private Communication Technology, SSL - Secure Sockets Layer, and so on), or several other stages in the processing of the HTTP request/response.
The setup program that installs the ISAPI filter should add it to the registry under HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\W3Svc\Parameters\Filter DLLs. This value is a comma-separated list. The filter should be added to the end of the list. When uninstalling the filter, remove the string that you added, taking care to handle the situation where other ISAPI filters have been added or removed since your DLL was installed.
Building an ISAPI Filter
An ISAPI filter can easily be built using Microsoft Visual Studio 6. A few easy clicks on MS VS6 produces a skeleton code of an empty ISAPI filter. First, open Microsoft Visual C++ of MS VS6. Open an ISAPI Extension Wizard at File>New>Projects, specifying a project name and location. Here I enter the project name as TryISAPI on the default location (C:\PROGRAM FILES\MICROSOFT VISUAL STUDIO\MYPROJECTS\TryISAPI).

As we're trying to build an ISAPI filter, select "Generate Filter Object" and deselect "Generate Server Extension Object". Change the filter class name and description if needed, else leave it in its default. Also select to build as a "a Shared DLL" for "How would you like to use the MFC library?"

On the second step, select the notification priority of the filter, the types of port that you're interested in, and the notifications that you need. Here, as we're trying to build a logging filter which would log all the request and response data of HTTP and HTTPS connections, I've subscribed to the "Incoming raw data and headers", "Outgoing raw data and headers" and "Post-processing of the request headers" notifications and set the notification priority to low which is the default.

That's it. When you finish it, you would get the skeleton of an empty simple ISAPI filter.
When you go through the CPP file (TryISAPI.cpp), you would find some of the important methods,
GetFilterVersion
OnPreprocHeaders
OnReadRawData
OnSendRawData
The GetFilterVersion
method would be called by the IIS while loading this ISAPI filter for setting up the necessary flags, the notifications, and its priority. The flags that can be set here are:
SF_NOTIFY_READ_RAW_DATA
flag is set to subscribe for the notification which is triggered when the IIS reads the request data when the IIS receives a HTTP(S) request.SF_NOTIFY_PREPROC_HEADERS
flag is set to subscribe for the notification which is triggered by the IIS processes the request headers for the incoming HTTP(S) request from the client.SF_NOTIFY_SEND_RAW_DATA
flag is set to subscribe for the notification which is triggered when the IIS sends the response data to the client after processing the request.SF_NOTIFY_AUTHENTICATION
flag is set to subscribe for the notification which is triggered by the IIS when it authenticates any HTTP(S) requests.SF_NOTIFY_URL_MAP
flag is set to subscribe for the notification which is triggered by the IIS maps the URL to the physical location. When the IIS encounters a HTTP(S) request, it translates the virtual address of the URL to the physical address/location. A notification is triggered when this translation takes when this flag is set.SF_NOTIFY_LOG
flag is set to subscribe for the notification which is triggered when the IIS begins to log the request in IIS log.SF_NOTIFY_END_OF_NET_SESSION
flag is set to subscribe for the notification which is triggered by the IIS when any client session terminates.SF_NOTIFY_ORDER_LOW
flag is used to set the priority of the filter and notifications to low. This is the default setting in MS VS6.SF_NOTIFY_ORDER_MEDIUM
flag is used to set the priority of the filter and notifications to medium.SF_NOTIFY_ORDER_HIGH
flag is used to set the priority of the filter and notifications to high.SF_NOTIFY_SECURE_PORT
flag is set to subscribe for the notification which is triggered by the IIS encounters any HTTP(S) request or response on the secure port. (e.g. 443 for SSL in case of HTTPS)SF_NOTIFY_NONSECURE_PORT
flag is set to subscribe for the notification which is triggered by the IIS encounters any HTTP request or response on the non-secure port. (E.g. 80 in case of HTTP)
Here, since we're developing an ISAPI filter of low priority for logging the HTTP & HTTPS request & response data, you would see only some of flags set in GetFilterVersion
method.
pVer->dwFlags |=
SF_NOTIFY_ORDER_LOW|SF_NOTIFY_SECURE_PORT|SF_NOTIFY_NONSECURE_PORT|
SF_NOTIFY_PREPROC_HEADERS|SF_NOTIFY_READ_RAW_DATA|SF_NOTIFY_SEND_RAW_DATA;
Considerations
SF_NOTIFY_READ_RAW_DATA
and SF_NOTIFY_SEND_RAW_DATA
flags/notifications can be used in the ISAPI filter only when the filter is installed as a global filter in IIS for all web sites and NOT to a specific web site. I would be dealing with installation and un-installation of an ISAPI filter at the end of this article. Also in case of IIS 6.x, in order to use these two notifications/flags, the IIS should be configured to run in "IIS 5.0 Isolation Mode".
In order to configure IIS 6.x in "IIS 5.0 Isolation Mode",
- Open "Internet Information Services (IIS) Manager". (Goto Start>Run, type inetmgr and say OK)
- Expand the node with machine name to see the node "Web Sites". Right click and say "Properties"
- In the "Web Sites Properties", got to "Service" tab and check "Run WWW service in IIS 5.0 Isolation Mode"

The OnReadRawData
method is that which is called by the IIS while reading the raw data of the incoming client request. Since this notification is triggered before SF_NOTIFY_PREPROC_HEADERS
notification, we would read and the store the raw data in a global static buffer (CString strRequestRawData
).
char *Data;
Data = (char *) pRawData->pvInData;
strRequestRawData = Data;
return SF_STATUS_REQ_NEXT_NOTIFICATION;
The OnPreprocHeaders
method is that which is called when the IIS processes the header information of the HTTP(S) requests. So, here we'll be extracting header information such as client IP address, URL and request type (e.g. GET, POST). We would be logging this information and the request raw data into a file.
// Reading client URL
pHeaderInfo->GetHeader(pCtxt->m_pFC, "url", buffer, &buffsize)
strURL = buffer;
// Reading client IP address
pCtxt->GetServerVariable("REMOTE_ADDR", buffer, &buffsize)
strIP = buffer;
// Reading request type
pCtxt->GetServerVariable("REQUEST_METHOD", buffer, &buffsize)
strCmd = buffer;
where buffer is char buffer[4096]
and strURL
, strIP
, strCmd
are global static CString
.
The OnSendRawData
method would be called by the IIS when it sends the response data to the client after it has finished its processing. Here, we would be extracting the response raw data and writing into the file.
// Reading response raw data
CString strResponseRawData;
strResponseRawData.Format("%s", pRawData->pvInData);
Creating, writing and closing a file can be achieved as follows.
// Creating the log file
CFile LogFile;
char *cpFileName = (char *)(LPCTSTR)strFileName;
LogFile.Open(cpFileName, CFile::modeCreate |
CFile::modeNoTruncate |CFile::modeWrite | CFile::shareDenyWrite, NULL);
// Writing to the log file
LogFile.Write(strURL, strURL.GetLength());
// Closing the log file
LogFile.Close();
Installation and Un-Installation of ISAPI Filter
ISAPI filter can be installed for a particular web site or as a global filter for all web sites in the IIS. I would be guiding through the process of installing the ISAPI filter in IIS 6.x through this process, which is similar in IIS 5.x (with minor changes).
In order to install the ISAPI filter in IIS 6.0 as a global filter for all web sites present in the IIS,
- Open "Internet Information Services (IIS) Manager"
- Expand the node with machine name to see the node "Web Sites". Right click and say "Properties". To install to a particular web site, go to the properties of that web sites.
- In the "Web Sites Properties", got to "ISAPI Filters" tab and add the ISAPI filter DLL (TryISAPI.dll) specifying the filter name and the physical location.


You can also modify the order in which the ISAPI filters are executed if there are more than one registered filters by moving any registered filter up/down using the "Move Up" or "Move Down" buttons here.
In order to remove an ISAPI filter from IIS, select the filter in the "ISAPI Filters" tab and click "Remove"
History
August 03, 2007 - Baseline (Version 1.0)