CodeZine(コードジン)

特集ページ一覧

Microsoft Web Browserコントロールのセキュリティを制御する

スクリプトやActiveXコントロールの実行を制限する方法

  • LINEで送る
  • このエントリーをはてなブックマークに追加
2006/01/10 14:00
目次

IInternetSecurityManagerの実装

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

 ベースとなっているのは、上記で作成した単純なサンプルアプリケーションです。このサンプルのForm1クラスに直接IInternetSecurityManagerを実装してみます(IInternetSecurityManagerを実装したサンプルアプリケーションを「WebBrowserSecurity02」フォルダに入れてあります)。

IInternetSecurityManagerの実装サンプル
IInternetSecurityManagerの実装サンプル

 まず、IInternetSecurityManagerを実装するには、COMInteropのためのインターフェースの宣言や定数の定義などが必要になります。最低限必要なものを入れた「WebBrowserAPI.cs/WebBrowserAPI.vb」ファイルを用意しましたので、このファイルをプロジェクトに追加します。なお、このファイルの内容はCOM Interopに慣れていないとよく分からない部分が多くあると思いますが、あまり気にする必要はありません。他のプロジェクトでこのファイルを使う場合、名前空間名・クラス名などは修正する必要があると思いますが、それ以外の部分を修正することはあまりないと思います。

 それでは、Form1の実装内容を見ていきましょう。まず、IInternetSecurityManagerとは直接関係ありませんが、チェックボックスを2つ追加して[スクリプトの実行を許可するかどうか][ActiveXコントロールの実行を許可するかどうか]をオン/オフできるようにします。また、[リロード]ボタンを追加しました。

 続いて、Form1IInternetSecurityManagerを実際に実装します。IInternetSecurityManagerを実装するにはIServiceProviderIInternetSecurityManagerの2つのインターフェースを実装したクラスが必要になります。これらのインターフェースを1つのクラスに実装してもかまいませんし、別々のクラスに実装してもかまいません。ここでは両方のインターフェースを直接Form1に実装することにします。なお、後に紹介するInternetSecurityManagerHelperクラスは、これら2つのインターフェースをInternetSecurityManagerHelperクラスに実装し、Form1からそれを利用するようになっています。

 まずはクラス宣言部にインターフェースを追加します。

インターフェースの追加(C#)
public class Form1 : System.Windows.Forms.Form,
    WebBrowserAPI.IServiceProvider,
    WebBrowserAPI.IInternetSecurityManager
インターフェースの追加(VB.NET)
Public Class Form1
    Inherits System.Windows.Forms.Form
    Implements WebBrowserAPI.IServiceProvider
    Implements WebBrowserAPI.IInternetSecurityManager

 そして、コンストラクタに以下のコードを追加します。このコードは「私はIInternetSecurityManagerを実装しているから取りにきてね」と[Microsoft Web Browser]コントロールにお願いしているコードです。必要以上に複雑な手順が必要なように感じるかもしれませんが、これは「こういうもの」と思うしかありません。ただ、以下のコードで今回のサンプルに依存しているのは[axWebBrowser1]の部分だけです。それ以外の部分は毎回同じです。なお、この処理はaxWebBrowser1が初期化されたあとに行う必要がありますので、必ずInitializeComponentの呼び出しの後に記述するようにしてください。

コンストラクタ(C#)
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);
}
コンストラクタ(VB.NET)
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の知識が必要ですが、ほとんどの場合そのままコピーするだけです。

IServiceProviderインターフェースの実装(C#)
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;
}
IServiceProviderインターフェースの実装(VB.NET)
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を返して「デフォルトの処理を行う」ようにするだけでかまいません。

IInternetSecurityManagerインターフェースの実装(C#)
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;
}
IInternetSecurityManagerインターフェースの実装(VB.NET)
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で始まる整数値です。これを見ていただくと、いろいろなアクションが定義されていることが分かると思います。実行の許可・不許可は、出力引数であるpPolicyURLPOLICY_ALLOW(許可)、URLPOLICY_DISALLOW(不許可)などをセットすることによって返します。

 上記のコードでは、checkBox1.CheckedがTrueの場合にはスクリプト関連のアクションをすべて許可、Falseの場合には不許可にするようにしています。URLACTION_SCRIPTで始まるアクションは複数ありますが、それらすべて含むようなURLACTION_SCRIPT_MINURLACTION_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」フォルダに入れてあります。

 まず、Form1InternetSecurityManagerHelperクラスを保持するフィールドを追加します。

フィールドを追加(C#)
private InternetSecurityManagerHelper internetSecurityManagerHelper;
フィールドを追加(VB.NET)
Private internetSecurityManagerHelper As InternetSecurityManagerHelper

 続いて、コンストラクタにInternetSecurityManagerHelperを生成し、[Microsoft Web Browser]コントロールと関連付けるためのコードを追加します。また、ProcessUrlActionイベントのイベントハンドラを追加します。

コンストラクタ(C#)
public Form1()
{
    //
    // Windows フォーム デザイナ サポートに必要です。
    //
    InitializeComponent();

    //
    // TODO: InitializeComponent 呼び出しの後に、
    // コンストラクタ コードを追加してください。
    //

    this.internetSecurityManagerHelper = 
        new InternetSecurityManagerHelper();
    this.internetSecurityManagerHelper.Attach(this.axWebBrowser1);
    this.internetSecurityManagerHelper.ProcessUrlAction +=
        new ProcessUrlActionEventHandler(
            internetSecurityManagerHelper_ProcessUrlAction);
}
コンストラクタ(VB.NET)
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イベントのハンドラを追加します。

ProcessUrlActionイベントハンドラ(C#)
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;
}
ProcessUrlActionイベントハンドラ(VB.NET)
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を実装すればこのような事態を事前に防ぐことができます。

参考資料

  1. Introduction to URL Security Zones
  • LINEで送る
  • このエントリーをはてなブックマークに追加

著者プロフィール

  • 青柳 臣一(アオヤギ シンイチ)

    Twiter: https://twitter.com/ShinichiAoyagi 中学生のころにプログラミングの楽しさを知り、それ以来趣味として仕事としてプログラミングに関わるようになる。23才のときに株式会社ディーバを設立。CADソフトウエア、給水設備監視システムなどのオリジナルソフトの開発...

あなたにオススメ

All contents copyright © 2005-2021 Shoeisha Co., Ltd. All rights reserved. ver.1.5