IInternetSecurityManagerの実装
IInternetSecurityManagerは、[Microsoft Web Browser]コントロールのスクリプトやActiveXコントロールの実行許可・不許可を制御するための仕組みです。しかし、残念ながらこれは.NET Frameworkに含まれている機能ではありません。完全にCOMの世界の住民です。そのため、IInternetSecurityManagerについて完全に理解するためにはCOMやCOMInteropの知識が必要ですが、使用するだけであれば、さほど難しくはありません。以下では、まずIInternetSecurityManagerの実装方法の一例を解説します。そして、その後に、複雑な部分のほとんどをカプセル化したInternetSecurityManagerHelperクラスを紹介します。
ベースとなっているのは、上記で作成した単純なサンプルアプリケーションです。このサンプルのForm1クラスに直接IInternetSecurityManagerを実装してみます(IInternetSecurityManagerを実装したサンプルアプリケーションを「WebBrowserSecurity02」フォルダに入れてあります)。

まず、IInternetSecurityManagerを実装するには、COMInteropのためのインターフェースの宣言や定数の定義などが必要になります。最低限必要なものを入れた「WebBrowserAPI.cs/WebBrowserAPI.vb」ファイルを用意しましたので、このファイルをプロジェクトに追加します。なお、このファイルの内容はCOM Interopに慣れていないとよく分からない部分が多くあると思いますが、あまり気にする必要はありません。他のプロジェクトでこのファイルを使う場合、名前空間名・クラス名などは修正する必要があると思いますが、それ以外の部分を修正することはあまりないと思います。
それでは、Form1の実装内容を見ていきましょう。まず、IInternetSecurityManagerとは直接関係ありませんが、チェックボックスを2つ追加して[スクリプトの実行を許可するかどうか][ActiveXコントロールの実行を許可するかどうか]をオン/オフできるようにします。また、[リロード]ボタンを追加しました。
続いて、Form1にIInternetSecurityManagerを実際に実装します。IInternetSecurityManagerを実装するにはIServiceProviderとIInternetSecurityManagerの2つのインターフェースを実装したクラスが必要になります。これらのインターフェースを1つのクラスに実装してもかまいませんし、別々のクラスに実装してもかまいません。ここでは両方のインターフェースを直接Form1に実装することにします。なお、後に紹介するInternetSecurityManagerHelperクラスは、これら2つのインターフェースをInternetSecurityManagerHelperクラスに実装し、Form1からそれを利用するようになっています。
まずはクラス宣言部にインターフェースを追加します。
public class Form1 : System.Windows.Forms.Form, WebBrowserAPI.IServiceProvider, WebBrowserAPI.IInternetSecurityManager
Public Class Form1 Inherits System.Windows.Forms.Form Implements WebBrowserAPI.IServiceProvider Implements WebBrowserAPI.IInternetSecurityManager
そして、コンストラクタに以下のコードを追加します。このコードは「私はIInternetSecurityManagerを実装しているから取りにきてね」と[Microsoft Web Browser]コントロールにお願いしているコードです。必要以上に複雑な手順が必要なように感じるかもしれませんが、これは「こういうもの」と思うしかありません。ただ、以下のコードで今回のサンプルに依存しているのは[axWebBrowser1]の部分だけです。それ以外の部分は毎回同じです。なお、この処理はaxWebBrowser1が初期化されたあとに行う必要がありますので、必ずInitializeComponentの呼び出しの後に記述するようにしてください。
public Form1() { // // Windows フォーム デザイナ サポートに必要です。 // InitializeComponent(); // // TODO: InitializeComponent // 呼び出しの後に、コンストラクタ コードを追加してください。 // // Microsoft Web Browser コントロールの // ActiveX コントロール本体を取得 object ocx = this.axWebBrowser1.GetOcx(); // Microsoft Web Browser コントロールから IServiceProvider を取得 WebBrowserAPI.IServiceProvider ocxServiceProvider = ocx as WebBrowserAPI.IServiceProvider; // IServiceProvider.QueryService() を使って IProfferService を取得 IntPtr profferServicePtr; ocxServiceProvider.QueryService( ref WebBrowserAPI.SID_SProfferService, ref WebBrowserAPI.IID_IProfferService, out profferServicePtr); WebBrowserAPI.IProfferService profferService = Marshal.GetObjectForIUnknown(profferServicePtr) as WebBrowserAPI.IProfferService; // IProfferService.ProfferService() を使って // 自分を IInternetSecurityManager として提供 int cookie = 0; profferService.ProfferService( ref WebBrowserAPI.IID_IInternetSecurityManager, this, ref cookie); }
Public Sub New() MyBase.New() ' この呼び出しは Windows フォーム デザイナで必要です。 InitializeComponent() ' InitializeComponent() 呼び出しの後に初期化を追加します。 ' Microsoft Web Browser コントロールの ' ActiveX コントロール本体を取得 Dim ocx As Object = Me.AxWebBrowser1.GetOcx() ' Microsoft Web Browser コントロールから IServiceProvider を取得 Dim ocxServiceProvider As WebBrowserAPI.IServiceProvider = _ DirectCast(ocx, WebBrowserAPI.IServiceProvider) ' IServiceProvider.QueryService() を使って IProfferService を取得 Dim profferServicePtr As IntPtr = New IntPtr ocxServiceProvider.QueryService( _ WebBrowserAPI.SID_SProfferService, _ WebBrowserAPI.IID_IProfferService, profferServicePtr) Dim profferService As WebBrowserAPI.IProfferService = _ DirectCast(Marshal.GetObjectForIUnknown(profferServicePtr), _ WebBrowserAPI.IProfferService) ' IProfferService.ProfferService() を使って ' 自分を IInternetSecurityManager として提供 Dim cookie As Integer = 0 profferService.ProfferService( _ WebBrowserAPI.IID_IInternetSecurityManager, Me, cookie) End Sub
続いてIServiceProviderインターフェースのメソッドを実装します。IServiceProviderにはQueryServiceメソッドが1つあるだけです。上記で「私はIInternetSecurityManagerを実装しているから取りにきてね」という意味のコードをコンストラクタに追加しましたが、これによって今度は[Microsoft Web Browser]コントロールがこのメソッドを呼び出しますので、以下のように自分からIInternetSecurityManagerを取得して返すようにします。細かい意味を理解するにはCOMの知識が必要ですが、ほとんどの場合そのままコピーするだけです。
public int QueryService( ref Guid guidService, ref Guid riid, out IntPtr ppvObject) { ppvObject = IntPtr.Zero; if (guidService == WebBrowserAPI.IID_IInternetSecurityManager) { // 自分から IID_IInternetSecurityManager を // QueryInterface して返す IntPtr punk = Marshal.GetIUnknownForObject(this); return Marshal.QueryInterface(punk, ref riid, out ppvObject); } return HRESULT.E_NOINTERFACE; }
Public Function QueryService(ByRef guidService As System.Guid, _ ByRef riid As System.Guid, ByRef ppvObject As System.IntPtr) _ As Integer Implements WebBrowserAPI.IServiceProvider.QueryService ppvObject = IntPtr.Zero If guidService.CompareTo( _ WebBrowserAPI.IID_IInternetSecurityManager) = 0 Then ' 自分から IID_IInternetSecurityManager を ' QueryInterface して返す Dim punk As IntPtr = Marshal.GetIUnknownForObject(Me) Return Marshal.QueryInterface(punk, riid, ppvObject) End If Return HRESULT.E_NOINTERFACE End Function
最後にIInternetSecurityManagerインターフェースの各メソッドを実装します。IInternetSecurityManagerには、8つのメソッドがあります。今回はProcessUrlActionメソッドにのみ、意味のある実装をしてみます。実装しないメソッドは、以下のようにINET_E_DEFAULT_ACTIONを返して「デフォルトの処理を行う」ようにするだけでかまいません。
public int SetSecuritySite( WebBrowserAPI.IInternetSecurityMgrSite pSite) { return WebBrowserAPI.INET_E_DEFAULT_ACTION; } public int GetSecuritySite( WebBrowserAPI.IInternetSecurityMgrSite pSite) { return WebBrowserAPI.INET_E_DEFAULT_ACTION; } public int MapUrlToZone(String pwszUrl, out int pdwZone, int dwFlags) { pdwZone = 0; return WebBrowserAPI.INET_E_DEFAULT_ACTION; } public int GetSecurityId(string pwszUrl, byte[] pbSecurityId, ref uint pcbSecurityId, uint dwReserved) { return WebBrowserAPI.INET_E_DEFAULT_ACTION; } public int ProcessUrlAction(String pwszUrl, int dwAction, out byte pPolicy, int cbPolicy, byte pContext, int cbContext, int dwFlags, int dwReserved) { pPolicy = 0; if (WebBrowserAPI.URLACTION_SCRIPT_MIN <= dwAction && dwAction <= WebBrowserAPI.URLACTION_SCRIPT_MAX) { if (this.checkBox1.Checked) { pPolicy = WebBrowserAPI.URLPOLICY_ALLOW; } else { pPolicy = WebBrowserAPI.URLPOLICY_DISALLOW; } return HRESULT.S_OK; } else if ((WebBrowserAPI.URLACTION_ACTIVEX_MIN <= dwAction && dwAction <= WebBrowserAPI.URLACTION_ACTIVEX_MAX)) { if (this.checkBox2.Checked) { pPolicy = WebBrowserAPI.URLPOLICY_ALLOW; } else { pPolicy = WebBrowserAPI.URLPOLICY_DISALLOW; } return HRESULT.S_OK; } return WebBrowserAPI.INET_E_DEFAULT_ACTION; } public int QueryCustomPolicy(String pwszUrl, ref Guid guidKey, byte ppPolicy, int pcbPolicy, byte pContext, int cbContext, int dwReserved) { return WebBrowserAPI.INET_E_DEFAULT_ACTION; } public int SetZoneMapping(int dwZone, String lpszPattern, int dwFlags) { return WebBrowserAPI.INET_E_DEFAULT_ACTION; } public int GetZoneMappings(int dwZone, out System.Runtime.InteropServices.UCOMIEnumString ppenumString, int dwFlags) { ppenumString = null; return WebBrowserAPI.INET_E_DEFAULT_ACTION; }
Public Function GetSecurityId(ByVal pwszUrl As String, _ ByVal pbSecurityId() As Byte, _ ByRef pcbSecurityId As System.UInt32, _ ByVal dwReserved As System.UInt32) As Integer Implements _ WebBrowserAPI.IInternetSecurityManager.GetSecurityId Return WebBrowserAPI.INET_E_DEFAULT_ACTION End Function Public Function GetSecuritySite(ByRef pSite As _ WebBrowserAPI.IInternetSecurityMgrSite) As Integer _ Implements WebBrowserAPI.IInternetSecurityManager.GetSecuritySite Return WebBrowserAPI.INET_E_DEFAULT_ACTION End Function Public Function GetZoneMappings(ByVal dwZone As Integer, _ ByRef ppenumString As _ System.Runtime.InteropServices.UCOMIEnumString, _ ByVal dwFlags As Integer) As Integer _ Implements WebBrowserAPI.IInternetSecurityManager.GetZoneMappings ppenumString = Nothing Return WebBrowserAPI.INET_E_DEFAULT_ACTION End Function Public Function MapUrlToZone(ByVal pwszUrl As String, _ ByRef pdwZone As Integer, ByVal dwFlags As Integer) As Integer _ Implements WebBrowserAPI.IInternetSecurityManager.MapUrlToZone pdwZone = 0 Return WebBrowserAPI.INET_E_DEFAULT_ACTION End Function Public Function ProcessUrlAction(ByVal pwszUrl As String, _ ByVal dwAction As Integer, ByRef pPolicy As Byte, _ ByVal cbPolicy As Integer, ByVal pContext As Byte, _ ByVal cbContext As Integer, ByVal dwFlags As Integer, _ ByVal dwReserved As Integer) As Integer _ Implements WebBrowserAPI.IInternetSecurityManager.ProcessUrlAction If WebBrowserAPI.URLACTION_SCRIPT_MIN <= dwAction And _ dwAction <= WebBrowserAPI.URLACTION_SCRIPT_MAX Then If Me.CheckBox1.Checked Then pPolicy = WebBrowserAPI.URLPOLICY_ALLOW Else pPolicy = WebBrowserAPI.URLPOLICY_DISALLOW End If Return HRESULT.S_OK ElseIf WebBrowserAPI.URLACTION_ACTIVEX_MIN <= dwAction And _ dwAction <= WebBrowserAPI.URLACTION_ACTIVEX_MAX Then If Me.CheckBox2.Checked Then pPolicy = WebBrowserAPI.URLPOLICY_ALLOW Else pPolicy = WebBrowserAPI.URLPOLICY_DISALLOW End If Return HRESULT.S_OK End If Return WebBrowserAPI.INET_E_DEFAULT_ACTION End Function Public Function QueryCustomPolicy(ByVal pwszUrl As String, _ ByRef guidKey As System.Guid, ByVal ppPolicy As Byte, _ ByVal pcbPolicy As Integer, ByVal pContext As Byte, _ ByVal cbContext As Integer, ByVal dwReserved As Integer) _ As Integer Implements _ WebBrowserAPI.IInternetSecurityManager.QueryCustomPolicy Return WebBrowserAPI.INET_E_DEFAULT_ACTION End Function Public Function SetSecuritySite(ByVal pSite As _ WebBrowserAPI.IInternetSecurityMgrSite) As Integer _ Implements WebBrowserAPI.IInternetSecurityManager.SetSecuritySite Return WebBrowserAPI.INET_E_DEFAULT_ACTION End Function Public Function SetZoneMapping(ByVal dwZone As Integer, _ ByVal lpszPattern As String, ByVal dwFlags As Integer) As Integer _ Implements WebBrowserAPI.IInternetSecurityManager.SetZoneMapping Return WebBrowserAPI.INET_E_DEFAULT_ACTION End Function
IInternetSecurityManagerを実装して[Microsoft Web Browser]コントロールに登録しておくと、いろいろな状況ごとにIInternetSecurityManagerのメソッドが呼び出されるようになります。ProcessUrlActionメソッドは、いろいろな処理(アクション)について「実行していいかどうか」を確認するために[Microsoft Web Browser]コントロールから呼び出されます。どのようなアクションについて問い合わせているのかがdwAction引数に格納されていますので、この値に応じて実行の許可・不許可を判定することができます。dwAction引数の値は「WebBrowserAPI.cs/WebBrowserAPI.vb」のURLACTIONで始まる整数値です。これを見ていただくと、いろいろなアクションが定義されていることが分かると思います。実行の許可・不許可は、出力引数であるpPolicyにURLPOLICY_ALLOW(許可)、URLPOLICY_DISALLOW(不許可)などをセットすることによって返します。
上記のコードでは、checkBox1.CheckedがTrueの場合にはスクリプト関連のアクションをすべて許可、Falseの場合には不許可にするようにしています。URLACTION_SCRIPTで始まるアクションは複数ありますが、それらすべて含むようなURLACTION_SCRIPT_MIN、URLACTION_SCRIPT_MAXも定義されています。これを使ってスクリプト関連のアクションであるかどうかを判定するようにしています(ActiveXコントロールに関しても同様です)。
ところで、[Microsoft Web Browser]コントロールがProcessUrlActionメソッドを呼び出すのは、Navigateした後かリロードした後だけのようです。ですから、アクションのポリシーを変更するにはNavigateかリロード(Refresh2メソッド)する必要があるようです。
なお、「WebBrowserAPI.cs」のURLACTIONで始まる定数、URLPOLICYで始まる定数はC/C++用の「UrlMon.h」(Platform SDKなどに含まれます)から機械的に生成したものです。また、アクションの説明や、どのようなアクションがあり、それぞれにどのようなポリシーを返すことができるかは、『Introduction to URL Security Zones』などに情報があります(残念ながらこれらの情報は日本語化されていないようです)。
おまけ - InternetSecurityManagerHelperの作成
上記のコードから明らかなように、IInternetSecurityManagerの実装に必要なコードのほとんどの部分は定型的なものとなっています。そこで、これらの部分をまとめたInternetSecurityManagerHelperクラスを作成してみました。このクラスを使うとIInternetSecurityManagerへの対応がかなり簡単になります。
IInternetSecurityManagerHelperクラスおよびIInternetSecurityManagerHelperを使用したサンプルアプリケーションは「WebBrowserSecurity03」フォルダに入れてあります。
まず、Form1にInternetSecurityManagerHelperクラスを保持するフィールドを追加します。
private InternetSecurityManagerHelper internetSecurityManagerHelper;
Private internetSecurityManagerHelper As InternetSecurityManagerHelper
続いて、コンストラクタにInternetSecurityManagerHelperを生成し、[Microsoft Web Browser]コントロールと関連付けるためのコードを追加します。また、ProcessUrlActionイベントのイベントハンドラを追加します。
public Form1() { // // Windows フォーム デザイナ サポートに必要です。 // InitializeComponent(); // // TODO: InitializeComponent 呼び出しの後に、 // コンストラクタ コードを追加してください。 // this.internetSecurityManagerHelper = new InternetSecurityManagerHelper(); this.internetSecurityManagerHelper.Attach(this.axWebBrowser1); this.internetSecurityManagerHelper.ProcessUrlAction += new ProcessUrlActionEventHandler( internetSecurityManagerHelper_ProcessUrlAction); }
Public Sub New() MyBase.New() ' この呼び出しは Windows フォーム デザイナで必要です。 InitializeComponent() ' InitializeComponent() 呼び出しの後に初期化を追加します。 Me.internetSecurityManagerHelper = _ New InternetSecurityManagerHelper Me.internetSecurityManagerHelper.Attach(Me.AxWebBrowser1) Me.internetSecurityManagerHelper.ProcessUrlAction = _ New ProcessUrlActionEventHandler( _ AddressOf internetSecurityManagerHelper_ProcessUrlAction) End Sub
最後にProcessUrlActionイベントのハンドラを追加します。
private int internetSecurityManagerHelper_ProcessUrlAction( String pwszUrl, int dwAction, out byte pPolicy, int cbPolicy, byte pContext, int cbContext, int dwFlags, int dwReserved) { pPolicy = 0; if (WebBrowserAPI.URLACTION_SCRIPT_MIN <= dwAction && dwAction <= WebBrowserAPI.URLACTION_SCRIPT_MAX) { if (this.checkBox1.Checked) { pPolicy = WebBrowserAPI.URLPOLICY_ALLOW; } else { pPolicy = WebBrowserAPI.URLPOLICY_DISALLOW; } return HRESULT.S_OK; } else if ((WebBrowserAPI.URLACTION_ACTIVEX_MIN <= dwAction && dwAction <= WebBrowserAPI.URLACTION_ACTIVEX_MAX)) { if (this.checkBox2.Checked) { pPolicy = WebBrowserAPI.URLPOLICY_ALLOW; } else { pPolicy = WebBrowserAPI.URLPOLICY_DISALLOW; } return HRESULT.S_OK; } return WebBrowserAPI.INET_E_DEFAULT_ACTION; }
Private Function internetSecurityManagerHelper_ProcessUrlAction( _ ByVal pwszUrl As String, ByVal dwAction As Integer, _ ByRef pPolicy As Byte, ByVal cbPolicy As Integer, _ ByVal pContext As Byte, ByVal cbContext As Integer, _ ByVal dwFlags As Integer, ByVal dwReserved As Integer) As Integer If WebBrowserAPI.URLACTION_SCRIPT_MIN <= dwAction And _ dwAction <= WebBrowserAPI.URLACTION_SCRIPT_MAX Then If Me.CheckBox1.Checked Then pPolicy = WebBrowserAPI.URLPOLICY_ALLOW Else pPolicy = WebBrowserAPI.URLPOLICY_DISALLOW End If Return HRESULT.S_OK ElseIf WebBrowserAPI.URLACTION_ACTIVEX_MIN <= dwAction And _ dwAction <= WebBrowserAPI.URLACTION_ACTIVEX_MAX Then If Me.CheckBox2.Checked Then pPolicy = WebBrowserAPI.URLPOLICY_ALLOW Else pPolicy = WebBrowserAPI.URLPOLICY_DISALLOW End If Return HRESULT.S_OK End If Return WebBrowserAPI.INET_E_DEFAULT_ACTION End Function
まとめ
[Microsoft Web Browser]コントロールはとても便利なコントロールです。メールソフトやRSSリーダーなどに代表されるようなコンテンツの表示に、[Microsoft Web Browser]コントロールを使用しているアプリケーションも数多くあるように思います。しかし、意図せずスクリプトやActiveXコントロールが実行されてしまうと、セキュリティホールなど思わぬトラブルの原因となりかねません。IInternetSecurityManagerを実装すればこのような事態を事前に防ぐことができます。
