【テクニカル・上級編】【上級者】PowerPointのプレゼンテーション保存前イベント(PresentationBeforeSave)をフックし、特定のセキュリティ基準を満たさない場合に保存を強制キャンセルするセキュリティアドイン開発 – PowerPoint VBA解析バイブル

スポンサーリンク

PowerPointを要塞化せよ:`PresentationBeforeSave`をフックするセキュリティアドインの極限構築

組織における情報漏洩の多くは、高度なサイバー攻撃ではなく、人間の「うっかり」によって引き起こされる。機密情報を含むプレゼンテーション資料に「社外秘」のウォーターマークが抜けている、あるいは暗号化されていない状態で共有ドライブに保存される――これを防ぐために人間の注意力に頼るのは、エンジニアリングの敗北だ。

われわれはコードで縛る。PowerPointの保存処理そのものをインターセプトし、組織のセキュリティ基準を満たさないファイルを水際で阻止する。本稿では、`Application`オブジェクトのイベント駆動モデルを極限まで利用し、堅牢なセキュリティアドインを構築する手法を解説する。

—

1. アーキテクチャの全体像と設計思想

一般的なVBAマクロは、ワークブックやプレゼンテーションの内部にコードを埋め込む(`.pptm`)。しかし、セキュリティの強制が目的ならば、ユーザーがコードを削除・改ざんできては意味がない。ゆえに、本システムは「PowerPointアドイン(`.ppam`)」として常駐させ、さらにWindowsのAPIやクラスモジュールを駆使してメモリリークやイベントの多重発火を防ぐプロ仕様のアーキテクチャを採用する。

実装すべき防御要件

1. 保存の強制キャンセル: 基準を満たさない場合、`Save`および`SaveAs`のプロセスを即座に中断する。
2. 監査ログの出力: 誰が、いつ、どのような理由で保存を拒絶されたかをローカルまたはWindowsイベントログに記録する。
3. オブジェクトライフサイクルの完全制御: アドインのアンロード時にイベントハンドラがメモリ上に残存し、PowerPointを不安定にする現象(ゾンビオブジェクト化)を完全に防ぐ。

—

2. 実装コード:堅牢なイベント監視モジュール

ここからが核心である。PowerPointのアプリケーションレベルイベントを捕捉するには、クラスモジュールを用いて`Application`オブジェクトを`WithEvents`で宣言する必要がある。

步驟1: クラスモジュール `CAppEvents` の作成

このクラスが全ての保存リクエストを監視する。

‘ =========================================================================
‘ クラスモジュール名: CAppEvents
‘ 役割: PowerPointのアプリケーションイベントをフックし、保存前チェックを実行する
‘ =========================================================================
Option Explicit

Public WithEvents App As PowerPoint.Application

‘ PresentationBeforeSave イベント
Private Sub App_PresentationBeforeSave(ByVal Pres As PowerPoint.Presentation, Cancel As Boolean)
On Error GoTo ErrorHandler

‘ 1. ホワイトリスト(テンプレートやシステム管理用ファイル)の除外判定
If IsSystemOrTemplate(Pres) Then Exit Sub

‘ 2. セキュリティ監査の実行(例:機密区分のプロパティチェック)
If Not ValidateSecurityPolicy(Pres) Then
‘ 基準を満たさない場合、保存を強制キャンセル
Cancel = True

‘ ユーザーへの警告と監査ログの記録
MsgBox “【セキュリティ警告】” & vbCrLf & _
“このプレゼンテーションは組織のセキュリティ基準を満たしていないため、保存がキャンセルされました。” & vbCrLf & _
“・プロパティの「分類(Category)」に機密レベルが設定されているか確認してください。”, _
vbCritical + vbOKOnly, “Enterprise Security Guard”

Call WriteAuditLog(Pres.Name, “Save Denied: Policy Violation”)
End If

Exit Sub

ErrorHandler:
‘ 予期せぬエラーでPowerPoint自体を巻き添えにしないためのフェイルセーフ
Cancel = True
MsgBox “セキュリティモジュール内で致命的なエラーが発生しました: ” & Err.Description, vbCritical
End Sub

‘ — ヘルパー関数: システムファイル・テンプレートの判定 —
Private Function IsSystemOrTemplate(ByVal Pres As PowerPoint.Presentation) As Boolean
‘ アドインファイル(.ppam)や特定のマスターファイルは除外
If Right(LCase(Pres.Name), 5) = “.ppam” Then
IsSystemOrTemplate = True
Else
IsSystemOrTemplate = False
End If
End Function

‘ — ヘルパー関数: セキュリティポリシーの検証ロジック —
Private Function ValidateSecurityPolicy(ByVal Pres As PowerPoint.Presentation) As Boolean
Dim prop As DocumentProperty
Dim hasClassification As Boolean

hasClassification = False

On Error Resume Next
‘ 組み込みドキュメントプロパティの「分類」をチェック基準とする
For Each prop In Pres.BuiltInDocumentProperties
If prop.Name = “Category” Then
If Len(Trim(CStr(prop.Value))) > 0 Then
hasClassification = True
Exit For
End If
End If
Next prop
On Error GoTo 0

‘ ここにカスタムのチェックロジック(スライド内の特定ワード検索など)を拡張可能
ValidateSecurityPolicy = hasClassification
End Function

‘ — ヘルパー関数: 監査ログ記録 —
Private Sub WriteAuditLog(ByVal fileName As String, ByVal status As String)
Dim fso As Object
Dim logFile As Object
Dim logPath As String

On Error Resume Next ‘ ログ書き込み失敗で保存処理自体を止めない
Set fso = CreateObject(“Scripting.FileSystemObject”)
logPath = Environ(“USERPROFILE”) & “\AppData\Local\PowerPointSecurityAudit.log”

Set logFile = fso.OpenTextFile(logPath, 8, True) ‘ 追記モード
logFile.WriteLine Now & ” | File: ” & fileName & ” | Status: ” & status
logFile.Close

Set logFile = Nothing
Set fso = Nothing
End Sub

步驟2: 標準モジュール `MMain` によるライフサイクル管理

アドインが読み込まれた瞬間からイベントを捕捉させ、終了時にはメモリから綺麗に消し去るためのエントリーポイント。

‘ =========================================================================
‘ 標準モジュール名: MMain
‘ 役割: アドインのロード・アンロード時のイベントフック初期化とメモリ解放
‘ =========================================================================
Option Explicit

‘ グローバル領域でのインスタンス保持(スコープが消滅するとイベントが切れるため)
Public G_AppEvents As CAppEvents

‘ アドイン読み込み時に自動実行されるエントリポイント
Sub Auto_Open()
InitializeSecurityMonitor
End Sub

‘ アドイン有効化、または手動実行用
Sub InitializeSecurityMonitor()
If G_AppEvents Is Nothing Then
Set G_AppEvents = New CAppEvents
Set G_AppEvents.App = Application
End If
‘ デバッグ用(実運用ではコメントアウトを推奨)
‘ MsgBox “PowerPoint セキュリティ監視アドインが正常に稼働を開始しました。”, vbInformation
End Sub

‘ アドイン終了時・アンロード時のメモリ解放(ゾンビオブジェクト防止)
Sub Auto_Close()
TerminateSecurityMonitor
End Sub

Sub TerminateSecurityMonitor()
On Error Resume Next
Set G_AppEvents.App = Nothing
Set G_AppEvents = Nothing
End Sub

—

3. チーフアーキテクトが教える:実運用における極限の知見

このコードをそのまま導入するだけでは、現場の運用で必ず破綻する。シニアエンジニアとして考慮すべき「現場の罠と回避策」を共有しよう。

A. COMオブジェクトの参照リークとゾンビ化を防ぐ

VBAにおける最大の悪習は、`Application`や`Presentation`オブジェクトの参照を解放し忘れることである。特に`WithEvents`を使用している場合、インスタンスがメモリ上に残留し続けると、PowerPointを終了しても背後でプロセス(POWERPNT.EXE)が残り続ける「ゾンビプロセス問題」を引き起こす。

  • 対策: `Auto_Close`やアドインの無効化イベントで、必ず `Set G_AppEvents.App = Nothing` とインスタンスの明示的な破棄を行うこと。

B. バッチ処理やマクロ自動保存時のデッドロック回避

組織内の別システム(例:RPAやサーバーサイドの変換スクリプト)がPowerPointを自動制御し、サイレントにファイルを保存しようとした場合、上記の `MsgBox` が割り込むとシステムが無限待機状態(フリーズ)に陥る。

  • 対策: 自動化プロセスからの実行を検知するため、環境変数や一時フラグ(例: `Application.Run` の引数やカスタムプロパティ)をチェックし、ヘッドレス環境(UIなし)では `MsgBox` をバイパスして単に `Cancel = True` のみを返す分岐を必ず実装すること。

C. レジストリとトラストセンターの壁

現代のOfficeセキュリティモデルは極めて厳格である。署名のないアドインや、信頼できる場所(Trusted Location)に配置されていないアドインからのイベントフックは、Officeのマクロ設定によって黙殺されるか、警告なしにブロックされる。

  • 対策: 本番配備の際は、必ず企業内のデジタル証明書(コードサイニング証明書)でVBAプロジェクトに署名し、GPO(グループポリシー)を用いてアドインのパスを「信頼できる場所」として全端末に配布すること。

—

結び

VBAは、単なる「お絵描きマクロの自動化ツール」ではない。適切に設計されたイベント駆動アーキテクチャとシステム連携の知識があれば、企業のコンプライアンスを守る強力なセキュリティゲートウェイへと昇華させられる。

妥協のないコードで、あなたの組織のPowerPoint環境を要塞化せよ。

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