【実務・中級編】Application.EventsEnabledを遮断して安全にドキュメントを開く静的セキュリティ検査マクロ – Visio VBA解析バイブル

スポンサーリンク

悪意あるマクロの脅威からVisioドキュメントを守る:Application.EventsEnabledを制する静的セキュリティ検査マクロ

Visio VBA開発者の皆さん、こんにちは。プロジェクトリーダーの〇〇です。
皆さんは、日々業務効率化のためにVisio VBAを活用されていることと思います。しかし、その一方で、不審なVisioドキュメントに潜む悪意あるマクロによって、システムが乗っ取られたり、情報が漏洩したりするリスクに、どれだけ備えていますでしょうか?

今回は、そんな脅威から皆さんの開発環境を守るための、強力な「静的セキュリティ検査マクロ」の実装方法を、Visio VBAのオブジェクトモデルの深淵を覗きながら、実務でそのまま使えるプロダクションコード例を交えて、徹底的に解説していきます。

なぜ、単純な「ファイルを開く」だけでは不十分なのか?

普段、Visioでファイルを開く際、特に意識せずに`Documents.Open`メソッドを使っているのではないでしょうか。しかし、このメソッドは、ドキュメントに含まれるマクロを自動的に実行してしまう可能性があります。悪意あるマクロは、ドキュメントを開いた瞬間に実行され、以下のような恐ろしい動作を引き起こす可能性があります。

  • 自動実行されるマクロ(AutoExec, AutoOpenなど)によるシステムへの侵入: ユーザーの操作を介さずに、バックグラウンドで悪意のあるコードが実行される。
  • 図形配置イベント(ShapeAdded, ShapeSelectionChangedなど)を悪用した攻撃: 特定の図形が配置されたり、選択されたりしたタイミングで、意図しない処理が実行される。
  • 外部リソースへの不正アクセス: ネットワーク上の共有フォルダへの書き込み、あるいは外部サーバーへの情報送信など。
  • Visioアプリケーション自体の乗っ取り: ユーザーの操作を乗っ取り、意図しない操作を強制される。

これらのリスクを回避するためには、ファイルを開く前に、マクロの自動実行を完全に「遮断」し、安全な状態でドキュメントを解析できる仕組みが不可欠です。

Application.EventsEnabled:イベント処理の「オン/オフ」スイッチ

Visio VBAには、`Application.EventsEnabled` という非常に強力なプロパティが存在します。これは、Visioアプリケーション全体で発生する様々なイベント(マクロの実行、図形の操作など)の発生を一時的に「無効」にするためのスイッチです。

このプロパティを`False`に設定することで、ドキュメントを開く際に、それに付随するマクロが自動的に実行されるのを防ぐことができます。そして、ドキュメントの解析が完了したら、元の状態に`True`に戻すことで、通常のVisioの動作に戻すことができます。

安全なドキュメント開封プロセスの設計

この`Application.EventsEnabled`を効果的に活用した、安全なドキュメント開封プロセスを設計する際のポイントは以下の通りです。

1. イベントの遮断: `Application.EventsEnabled = False` を実行し、全てのイベント処理を無効化します。
2. ドキュメントのオープン: `Documents.Open`メソッドを使用して、ドキュメントを開きます。この際、マクロは実行されません。
3. 静的解析: 開かれたドキュメントに対して、必要なセキュリティ検査(マクロの存在確認、怪しいコードのパターン検出など)を実施します。
4. ドキュメントのクローズ(オプション): 解析後、不要であればドキュメントを閉じます。
5. イベントの復旧: `Application.EventsEnabled = True` を実行し、イベント処理を元に戻します。
6. エラーハンドリング: 処理中に予期せぬエラーが発生した場合でも、必ずイベント処理が復旧されるように、厳格なエラーハンドリングを実装します。

プロダクションコード例:静的セキュリティ検査マクロ

それでは、この設計思想に基づいた、実践的なVBAコード例を見ていきましょう。このコードは、指定されたVisioファイルを開き、マクロの自動実行を遮断した状態で、そのマクロの有無を確認するものです。

Option Explicit

‘===============================================================================
‘ Sub: SafeOpenAndScanVisioDocument
‘ 目的: 指定されたVisioドキュメントを、マクロの自動実行を遮断した状態で開き、
‘ マクロの存在を静的に検査します。
‘ 引数:
‘ – filePath: 検査対象のVisioドキュメントのフルパス (String)
‘ 戻り値:
‘ – Boolean: マクロが存在した場合 True, 存在しない場合 False
‘===============================================================================
Public Function SafeOpenAndScanVisioDocument(ByVal filePath As String) As Boolean

Dim visApp As Visio.Application
Dim visDoc As Visio.Document
Dim hasMacros As Boolean
Dim originalEventsEnabledState As Boolean

‘ 現在のApplication.EventsEnabledの状態を保存しておく
‘ これは、処理完了後に元の状態に戻すために重要です。
originalEventsEnabledState = Application.EventsEnabled

‘ イベント処理を一時的に無効化する
‘ これにより、ドキュメントを開く際にマクロが自動実行されるのを防ぎます。
Application.EventsEnabled = False

On Error GoTo ErrorHandler ‘ エラーハンドリングを設定

‘ 安全な状態でドキュメントを開く
‘ EventsEnabledがFalseなので、ドキュメント内のマクロは実行されません。
Set visDoc = Application.Documents.Open(filePath)

‘ ドキュメント内にマクロが存在するかどうかを検査する
‘ visDoc.VBProject.VBComponentsコレクションに要素があればマクロが存在します。
‘ 悪意あるマクロの特定には、さらに詳細なコード解析が必要ですが、
‘ ここでは単純な存在確認に留めます。
If visDoc.HasVBProject Then
If Not visDoc.VBProject Is Nothing Then
If visDoc.VBProject.VBComponents.Count > 0 Then
hasMacros = True
End If
End If
End If

‘ マクロが存在する場合、より詳細な分析に進む(ここでは省略)
If hasMacros Then
MsgBox filePath & ” にはマクロが含まれています。”, vbExclamation, “マクロ検出”
‘ TODO: ここに、マクロコードの静的解析(キーワード検索、不正なAPI呼び出しの検出など)
‘ を実装するロジックを追加します。
‘ 例えば、visDoc.VBProject.VBComponents(“ThisDocument”).CodeModule.Lines(1, visDoc.VBProject.VBComponents(“ThisDocument”).CodeModule.CountOfLines)
‘ のようにしてコードを取得し、文字列検索などを行います。
Else
MsgBox filePath & ” にはマクロは含まれていません。”, vbInformation, “マクロ検出”
End If

‘ ドキュメントを閉じる(必要に応じて)
‘ 解析が終わったら、メモリを解放するために閉じるのが一般的です。
visDoc.Close

‘ 処理が正常に完了したら、関数としてTrueを返す
SafeOpenAndScanVisioDocument = hasMacros

ExitHere:
‘ 処理が正常に終了した場合も、エラーハンドリングから抜ける前に必ず実行される
‘ 重要なのは、Application.EventsEnabled を元の状態に戻すことです。
‘ これを忘れると、Visioアプリケーション全体のイベント処理が無効になったままになり、
‘ 予期せぬ不具合を引き起こします。
Application.EventsEnabled = originalEventsEnabledState
Set visDoc = Nothing
Set visApp = Nothing
Exit Function

ErrorHandler:
‘ エラーが発生した場合の処理
MsgBox “エラーが発生しました: ” & Err.Description, vbCritical, “エラー”

‘ エラーが発生した場合でも、必ずApplication.EventsEnabledを元に戻す
‘ これにより、Visioアプリケーションの正常な動作を保証します。
Application.EventsEnabled = originalEventsEnabledState

Set visDoc = Nothing
Set visApp = Nothing
‘ エラーが発生した場合は、Falseを返すか、エラーコードを返すなどの処理を行う
‘ ここでは、エラー発生時はマクロが存在しなかったものとしてFalseを返します。
SafeOpenAndScanVisioDocument = False
Resume ExitHere ‘ ExitHereラベルにジャンプして後処理を実行

End Function

‘===============================================================================
‘ Sub: CallSafeScanExample
‘ 目的: SafeOpenAndScanVisioDocument関数を呼び出すためのサンプルプロシージャです。
‘ 実際の使用時には、ファイルパスを適切に設定してください。
‘===============================================================================
Sub CallSafeScanExample()

Dim targetFilePath As String

‘ 検査したいVisioドキュメントのフルパスを指定してください。
‘ 例: targetFilePath = “C:\Users\YourUser\Documents\MySecureDiagram.vsdx”
targetFilePath = Application.GetOpenFileName(“Visio Files (.vsd;.vsdx;.vsdm),.vsd;.vsdx;.vsdm”, 1, “Visioファイルを開く”)

If targetFilePath = “False” Then ‘ ユーザーがキャンセルした場合
MsgBox “ファイル選択がキャンセルされました。”, vbInformation
Exit Sub
End If

‘ 安全な方法でドキュメントを開き、マクロの存在を検査する
Dim macroFound As Boolean
macroFound = SafeOpenAndScanVisioDocument(targetFilePath)

‘ 結果の表示(必要に応じて)
If macroFound Then
‘ マクロが見つかった場合の追加処理
‘ 例: ログファイルへの記録、管理者に通知など
Else
‘ マクロが見つからなかった場合の処理
End If

End Sub

コード解説とプロダクションコードのポイント

1. `originalEventsEnabledState`による状態の保持

originalEventsEnabledState = Application.EventsEnabled

この一行は、極めて重要です。
Visioアプリケーションは、他のプロセスやユーザーからの操作によって、`EventsEnabled`の状態が変更される可能性があります。したがって、一時的に`False`にする前に、現在の状態を必ず保存しておき、処理の最後に元の状態に戻すことが、堅牢な設計の鉄則です。これを怠ると、あなたのマクロが原因で、他のマクロやVisioの標準機能が正常に動作しなくなるという、最悪のシナリオを招きかねません。

2. `On Error GoTo ErrorHandler`と`ErrorHandler`ラベル

On Error GoTo ErrorHandler ‘ エラーハンドリングを設定
‘ … (処理コード) …
ExitHere:
‘ … (後処理) …
Exit Function

ErrorHandler:
‘ … (エラー処理) …
Resume ExitHere

`Application.EventsEnabled = False`にした後、ファイルが開けなかったり、その他の予期せぬエラーが発生したりする可能性は常にあります。
`On Error GoTo`ステートメントは、エラー発生時に指定したラベル(ここでは`ErrorHandler`)に処理をジャンプさせるための仕組みです。
`ErrorHandler`ラベル内では、エラーメッセージを表示し、最も重要なこととして `Application.EventsEnabled = originalEventsEnabledState` を再度実行し、イベント処理を必ず復旧させています。
そして、`Resume ExitHere`によって、後処理(`ExitHere`ラベル以降のコード)を実行してから、プロシージャを終了させます。

3. `visDoc.HasVBProject`と`VBProject.VBComponents.Count`によるマクロの存在確認

If visDoc.HasVBProject Then
If Not visDoc.VBProject Is Nothing Then
If visDoc.VBProject.VBComponents.Count > 0 Then
hasMacros = True
End If
End If
End If

`visDoc.HasVBProject`プロパティは、ドキュメントがVBプロジェクト(マクロコード)を含んでいるかどうかを`True`/`False`で返します。
さらに、`visDoc.VBProject.VBComponents.Count`で、実際に含まれるコンポーネント(標準モジュール、クラスモジュール、ThisDocumentなど)の数を取得できます。この数が0より大きければ、マクロが存在すると判断できます。

注意点: このコードは、あくまで「マクロコードが存在するかどうか」を判定するものです。悪意あるコードが文字列として埋め込まれている場合や、難読化されている場合、あるいは`VBProject`自体が存在しない(しかし、外部から呼び出される可能性のある)シナリオには対応していません。 より高度なセキュリティ検査には、`CodeModule`プロパティを使用してコード文字列を取得し、正規表現などを用いた詳細なパターンマッチングや、既知の悪意あるコード片との比較が必要になります。

4. `Documents.Open`メソッドの挙動

`Application.EventsEnabled = False`の状態で`Documents.Open`を実行すると、Visioはドキュメントを開くだけで、それに含まれるマクロ(`AutoExec`や`AutoOpen`など)を自動実行しません。これは、静的解析を行う上での最大のメリットです。

5. `visDoc.Close`とメモリ管理

visDoc.Close

解析が終わったドキュメントは、不要であれば`Close`メソッドで閉じ、メモリを解放するのが良いプラクティスです。特に、多数のファイルを連続して処理する場合、メモリリークの原因となるため注意が必要です。

6. `Application.GetOpenFileName`による安全なファイル選択

`CallSafeScanExample`サブプロシージャでは、`Application.GetOpenFileName`メソッドを使用して、ユーザーにVisioファイルを選択させるようにしています。これにより、コード内に直接ファイルパスをハードコーディングするリスクを減らし、より柔軟な利用を可能にしています。

ファイル連携・データベース連携の注意点

この静的セキュリティ検査マクロを、より広範な業務自動化ツールに組み込む際には、以下の点に十分注意してください。

  • ファイルパスの安全性: ユーザーからの入力や、外部システムからのファイルパスは、必ず検証してください。無効なパスや、システムファイルを指定された場合、予期せぬエラーやセキュリティリスクにつながります。
  • 外部データベースからのファイルパス取得: データベースに保存されているVisioファイルのパスを基に処理を行う場合、そのパス情報が改ざんされていないか、権限のないユーザーがアクセスできないか、といった点も考慮する必要があります。
  • ログ記録: 検査結果(マクロの検出有無、エラー情報など)は、必ずログファイルに記録してください。これにより、後から監査やトラブルシューティングを行う際に役立ちます。
  • 権限管理: このマクロを実行するユーザーには、対象となるVisioファイルへの読み取り権限が必要です。また、マクロコード自体に書き込み権限が必要な場合(例:検査結果をファイルに書き込む場合)は、その権限も考慮してください。

まとめ

`Application.EventsEnabled`プロパティは、Visio VBAにおけるイベント処理の強力な制御手段であり、悪意あるマクロからシステムを守るための第一歩です。
今回紹介した静的セキュリティ検査マクロは、その基本的な実装方法を示したものです。
このテクニックを理解し、自らの開発プロセスに組み込むことで、より安全で堅牢なVisio VBAアプリケーションを構築できるはずです。

開発プロジェクトのリーダーとして、皆さんが常にセキュリティを意識し、より質の高いツール開発に取り組むことを期待しています。

それでは、次回の記事でお会いしましょう。

タイトルとURLをコピーしました