ISAPI FilterでHttpOnly属性を設定する


作者:Tony Qu
ISAPIといえば、ASPをやっている人が多いので、よく知らないと思います.NETが開発されると、ISAPIの方式はすでに時代遅れで、代わりにHttpHandlerとHttpModuleがあり、この2つのものといえば多くの人が理解しているだろうが、ISAPIは早期に要求ブロックと処理を実現する唯一の道と言えるが、ASP.NETの流行は、だんだん開発者の視野から薄れてきた.
 
この文の開発シーンはこうです.うちの会社は古いASP言語を使っていますが、ASPのResponse.Cookies属性にはHttpOnlyはありません(ただし.NetのCookieオブジェクトにはHttpOnly属性があります)、Path属性を利用してHttpOnlyを設定できるという投稿があります.これは、ページでcookie値を設定する動作がSet-Cookieヘッダに変換されるためです.以下のようにします.
Set-Cookie: user=t=bfabf0b1c1133a822; path=/
     cookie  HttpOnly,        :
Set-Cookie: user=t=bfabf0b1c1133a822; path=/;HttpOnly

理論的にはPathを設定することは完全に可能で、はっきり言って元のPathの基礎の上で増加するためです;HttpOnly,
しかし、私は次のコードを使っています.
Response.Cookies(“user”).Path+=”;HttpOnly”;
得られた結果は
Set-Cookie: user=t=bfabf0b1c1133a822; path=/3B%;HttpOnly
これは明らかにだめなので、ISAPIで実現することを考えなければなりません.
 
ISAPIベース
まず、ISAPI ExtensionとISAPI Filterを混同しないでください.この2つのものは1文字差ですが、完全に2つのもので、提供されるインタフェースは全く違います.ISAPI Extensionは似たようなページのdllで、postまたはgetコミットなどを行うことができます.http://localhost/abc.dll?a=1厳密な意味ではブロック機能がなく、cgiとは差が少ない.一方、ISAPI Filterにはフィルタ機能があり、IISサイトの属性にaspなどのロードが必要なISAPI Filterを追加することができます.Netの実装では、aspnet_というISAPI Filterも使用されています.filter.dll.
ISAPI FilterはあくまでDLLであり、GetFilterVersionとHttpFilterProcの2つの主要なインタフェースがあります.以下に示します.
BOOL WINAPI __stdcall GetFilterVersion(HTTP_FILTER_VERSION *pVer) 
{ 
 /* Specify the types and order of notification */ 
 
 pVer->dwFlags = (SF_NOTIFY_PREPROC_HEADERS | SF_NOTIFY_AUTHENTICATION | 
 SF_NOTIFY_URL_MAP | SF_NOTIFY_SEND_RAW_DATA | SF_NOTIFY_LOG | SF_NOTIFY_END_OF_NET_SESSION ); 
 
 pVer->dwFilterVersion = HTTP_FILTER_REVISION; 
 
 strcpy(pVer->lpszFilterDesc, "Upper case conversion filter, Version 1.0"); 
 
 CFile myFile("c:\\mylist.html", CFile::modeCreate | CFile::modeWrite);
 myFile.SeekToEnd();
 char myText[40];
 strcpy(myText,"<B>GetFilterVersion </B><BR><BR>");
 myFile.Write(myText,strlen(myText));
 myFile.Close();
 
 return TRUE; 
}

GetFilterVersionは、Filterバージョンを取得するだけでなく、トリガが必要なイベントをフィルタリングするために使用できます.これらのイベントの詳細は、参照してください.http://msdn.microsoft.com/en-us/library/ms825957.aspx.ここでやっているのは操作ではなく、操作であることに注意してください.数理論理を学んだことがある人は、これが何に使われているのかを理解しなければなりません.値の重ね合わせです.もっと直接に言ってください.EventA|EventBは私がEvent AもEvent Bも必要です.
DWORD WINAPI __stdcall HttpFilterProc(HTTP_FILTER_CONTEXT *pfc, DWORD NotificationType, VOID *pvData) 
{ 
 CFile myFile("c:\\mylist.html", CFile::modeWrite);
 myFile.SeekToEnd();
 
 switch (NotificationType) { 
 
 case SF_NOTIFY_ACCESS_DENIED : 
 
 myFile.Write("SF_NOTIFY_ACCESS_DENIED<BR>",strlen("SF_NOTIFY_ACCESS_DENIED<BR>"));
 break;
 
 case SF_NOTIFY_AUTH_COMPLETE :
 
 myFile.Write("SF_NOTIFY_AUTH_COMPLETE<BR>",strlen("SF_NOTIFY_AUTH_COMPLETE<BR>"));
 break;
 
 case SF_NOTIFY_AUTHENTICATION :
 
 myFile.Write("SF_NOTIFY_AUTHENTICATION<BR>",strlen("SF_NOTIFY_AUTHENTICATION<BR>"));
 break;
 
 case SF_NOTIFY_END_OF_NET_SESSION : 
 
 myFile.Write("SF_NOTIFY_END_OF_NET_SESSION<BR>",strlen("SF_NOTIFY_END_OF_NET_SESSION<BR>"));
 break;
 
 case SF_NOTIFY_END_OF_REQUEST : 
 
 myFile.Write("SF_NOTIFY_END_OF_REQUEST<BR>",strlen("SF_NOTIFY_END_OF_REQUEST<BR>"));
 break;
 
 case SF_NOTIFY_LOG : 
 
 myFile.Write("SF_NOTIFY_LOG<BR>",strlen("SF_NOTIFY_LOG<BR>"));
 break;
 
 case SF_NOTIFY_PREPROC_HEADERS : 
 
 myFile.Write("SF_NOTIFY_PREPROC_HEADERS<BR>",strlen("SF_NOTIFY_PREPROC_HEADERS<BR>"));
 break;
 
 case SF_NOTIFY_READ_RAW_DATA : 
 
 myFile.Write("SF_NOTIFY_READ_RAW_DATA<BR>",strlen("SF_NOTIFY_READ_RAW_DATA<BR>"));
 break;
 
 case SF_NOTIFY_SEND_RAW_DATA :
 
 myFile.Write("SF_NOTIFY_SEND_RAW_DATA<BR>",strlen("SF_NOTIFY_SEND_RAW_DATA<BR>"));
 break;
 
 case SF_NOTIFY_SEND_RESPONSE : 
 
 myFile.Write("SF_NOTIFY_SEND_RESPONSE<BR>",strlen("SF_NOTIFY_SEND_RESPONSE<BR>"));
 break;
 
 case SF_NOTIFY_URL_MAP : 
 
 myFile.Write("SF_NOTIFY_URL_MAP<BR>",strlen("SF_NOTIFY_URL_MAP<BR>"));
 break;
 
 case SF_NOTIFY_SECURE_PORT : 
 
 myFile.Write("SF_NOTIFY_SECURE_PORT<BR>",strlen("SF_NOTIFY_SECURE_PORT<BR>"));
 break;
 
 case SF_NOTIFY_NONSECURE_PORT :
 
 myFile.Write("SF_NOTIFY_NONSECURE_PORT<BR>",strlen("SF_NOTIFY_NONSECURE_PORT<BR>"));
 break;
 
 default : 
 break; 
 } 
 
 
 myFile.Close();
 
 return SF_STATUS_REQ_NEXT_NOTIFICATION; 
}

HttpFilterProcは、コンソールプログラムのmainに相当するメインエントリです.上記のコードは、これらのイベントがトリガーされたときにログに書き込まれ、デバッグが容易になります.
ここでは、通常ISAPI Filterを開発するプロセスについて説明します.
a.既存のISAPI Filterプロジェクトを獲得し、テンプレートとして、このネット上にはたくさんあります.googleはすぐにあります.
b.GetFilterVersionのdwFlagsの値を変更して、必要なイベントを決定する
c.HttpFilterProcのcaseブランチを修正し、不要なイベントを削除する
d.処理が必要なイベントにコードを書く.
 
ISAPIを書くときは、この2つのインタフェース、すなわちDLLを定義するEXPORTSを忘れないでください.以下のようにします.
LIBRARY "isapi_sample"
EXPORTS
HttpFilterProc
GetFilterVersion

 
イベントの実行順序
ASP.NETにはPage Life Cycleがあり、ISAPI Filterも同様であり、これらの時間の実行順序はhttp://msdn.microsoft.com/en-us/library/ms524855(VS.90).aspxで見つかりました.次のイベントは実行順に並べられています.
SF_NOTIFY_READ_RAW_DATA
SF_NOTIFY_PREPROC_HEADERS
SF_NOTIFY_URL_MAP 
SF_NOTIFY_AUTHENTICATION
SF_NOTIFY_AUTH_COMPLETE
SF_NOTIFY_SEND_RESPONSE
SF_NOTIFY_SEND_RAW_DATA
SF_NOTIFY_END_OF_REQUEST
SF_NOTIFY_LOG
SF_NOTIFY_END_OF_NET_SESSION
解析により、Set-Cookie headerを取得するには、ASPがページを処理した後に、ASPページコードがCookie値を設定する可能性があるため、SF_NOTIFY_PREPROC_HEADERSイベントは、要求を受信した後、ページを処理する前にトリガーされるため、ページの処理が完了し、送信前にトリガーされるイベントが必要であるため、SF_NOTIFY_SEND_RESPONSEが最適です.次のセクションでは、このイベントに処理コードを追加する方法について説明します.
 
Set-Cookieを巡る方法
HttpFilterProc関数の3番目のパラメータVOID*pvDataはイベントに対応するデータで、ヘッダの中のデータを得るためにPHTTP_に変換しますFILTER_PREPROC_HEADERSは、Set-cookieのデータを読み出してから処理しなければならないからです.
コードは次のとおりです.
 1: case SF_NOTIFY_SEND_RESPONSE : 
 2: pPH = (PHTTP_FILTER_PREPROC_HEADERS)pvData;
 3: pPH->GetHeader(pfc, "Set-Cookie:", szBuffer, &dwSize);
 4:  
 5: cookieNum=sizeof(strtok(szBuffer,","));
 6: if(cookieNum>0)
 7: {
 8: //handle the cookies that are read from header
 9: ...
 10: }

ここのszBufferは私たちが得たSet-Cookieの文字列です.ここではSet-Cookieが何なのかを説明します.多くのプログラマーはSet-Cookieの意味と表現形式を特に理解していないからです.
ページにCookie値を設定するたびに、ASPでもASPでもNETでは、設定された操作をSet-Cookieの文字列に変換します.これらの要求をFiddlerまたはHttpFoxで追跡すると、頭の中にSet-Cookie項目があります.これはCookieを設定する操作がある場合にのみあります.また、Set-Cookieの各Cookie文字列は、次のようにカンマで区切られています.Set-Cookie: test1=a; path=/ , test2=b; path=/
ここではtest 1とtest 2という2つのcookie値を設定し、1つのcookieの属性の間にセミコロンで区切られています.そのため、このコードではstrtokを使用して、Set-cookieのcookie文字列の総数(文字の総数ではないことに注意)を表す文字列の各セグメントをカンマで区切ったcookie文字列を取得します.
クッキーの文字列を1つずつ手に入れると、HttpOnlyはこれらの文字列の最後に付加され、最終的に文字列をつなぎ合わせてSet-Cookie文字列を構成し、文字列のつなぎ合わせ方法についてはあまり話さないが、これは完全にC++実現の問題である.
 
Set-Cookie文字列を上書きする方法
ここでの設定クッキーは私たちが普段コードでやっているのとは違います.私たちが要求中のSet-Cookieを直接修正しなければならないからです.新しいSet-Cookieを増やすのではなく、修正したのは、Set-Cookieが要求したヘッダーの中に1つしかないからです.
HTTP_FILTER_SEND_RESPONSE * pResponse=(HTTP_FILTER_SEND_RESPONSE *)pvData;
BOOL fServer = TRUE;
fServer = pResponse->SetHeader(pfc, "Set-Cookie:",szHeader);

上のコードはpvDataをHTTP_に変換しますFILTER_SEND_RESPONSEタイプは、Responseを操作し、そのSetHeaderメソッドを呼び出すことでSet-Cookie headerを設定できます.
 
完全コードダウンロード:isapi_sample.zip(VC 6プロジェクト)、最も主要なのはMyISAPIです.cppとMyISAPI.defファイル、その他はエンジニアリングファイルです.