IIS – Rejecting a request from a specific client type(browser) | ISAPI Filter Example

Recently I’ve come across a discussion where a particular type of client request should be blocked. Say for an example, you need to block requests from a client called “TrustMe”; consider a scenario where you need to serve pages only for Internet Explorer 7 clients, not IE6.0 clients. This kind of requests are not so common, but there would be someone who may need this. Hence, this blog post :-)

So, first of all we need to understand how we can differentiate between client browsers? How does a server identify what is the client browsing the site? It can identify through “User-Agent” request header. Example of an User-Agent sent from my IE6 client is below:

User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.2; SV1; .NET CLR 1.1.4322; .NET CLR 2.0.50727; .NET CLR 3.0.04506.648; .NET CLR 3.5.21022)

So, if you are able to check if your “TrustMe” client is a part of this header, you’ll be able to reject the response with 404 or whatever you want to do. You should now be remembering about URLScan’s [DenyHeaders] section helping here?

You cannot use that to reject requests with “User-Agent: TrustMe”. AFAIK, that section is used to reject *any* requests coming with the mentioned header. It means, you can only decide on the header name, not the value of the header itself.

Below is an example:

[DenyHeaders]

My-Header:

This will reject *any* requests, that have a request header of “My-Header:” regardless of its value. So, all of your requests will be rejected if you add “User-Agent:” to the [DenyHeaders].

AFAIK, only a custom ISAPI Filter can reject a response (with 404 or whatever) if coming from a specific user agent. Below is an example code to check this:

 DWORD WINAPI __stdcall HttpFilterProc(HTTP_FILTER_CONTEXT *pfc, DWORD NotificationType, VOID *pvData) 
{ 
    char buffer[256];
    DWORD buffSize = sizeof(buffer);
    HTTP_FILTER_PREPROC_HEADERS *p;
 
    switch (NotificationType)  {

      case SF_NOTIFY_PREPROC_HEADERS :
      p = (HTTP_FILTER_PREPROC_HEADERS *)pvData;
      BOOL bHeader = p->GetHeader(pfc,"User-Agent:",buffer,&buffSize); 
      CString UserAgent(buffer);
      if(UserAgent.Find("TrustMe") != -1) {
        // Found; so changing the URL to be an unavailable URL, so your request would be 
        // rejected with 404; just as URLSCAN does
        p->SetHeader(pfc, "url", "/Rejected-coz-of-Restricted-UserAgent");
      }
      return SF_STATUS_REQ_HANDLED_NOTIFICATION; 
    }
    return SF_STATUS_REQ_NEXT_NOTIFICATION; 
}

But, this is always less secure, because User-Agent is easily configurable.  So, if you are planning to stop your site being used by some “attacking” user-agent, you can try using this. But, most of the time attackers are little intelligent than what we think them as, so they can easily change the request header. You can also think your own logic to accept only the clients you want to, rejecting the rest.

So, if you want your ISAPI filter to reject requests from any IE6, you just need to check if the User-Agent header has the string “MSIE 6.0”. If you want help in writing an ISAPI filter, check my earlier blog posts here.

Feel free to use the above code, but use at your own risk.