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

スポンサーリンク
スポンサーリンク

データ漏洩を水際で阻止せよ:PresentationBeforeSaveを掌握する堅牢なセキュリティアドイン設計論

ビジネスの現場において、プレゼンテーション資料は「武器」であると同時に、一歩間違えれば「致命的な機密漏洩の導火線」となる。

多くの開発者が「特定の文字列が含まれていたら警告を出す」程度の単純なマクロを書いて満足しているが、それは設計思想として極めて脆弱だ。ユーザーがそのマクロを無効化したり、あるいはマクロが含まれない新規ファイルで作業を始めたりした瞬間に、防壁は崩壊する。

我々プロフェッショナルが目指すべきは、ユーザーの操作を「監視」するのではなく、アプリケーションのライフサイクルそのものに「規律」を組み込むアドイン(Add-in)の構築である。

本稿では、PowerPointの`PresentationBeforeSave`イベントをフックし、セキュリティ基準を満たさない保存を物理的に阻止する、プロダクションレベルの設計手法を伝授する。

1. アーキテクチャの選定:なぜ「マクロ」ではなく「アドイン」なのか

まず、設計の根幹を理解せよ。通常の`.pptm`ファイルに記述されたコードは、そのファイルが開いている間しか機能しない。これは「点」の防御だ。

対して、`.ppam`(PowerPoint Add-in)形式で配布するアドインは、PowerPointアプリケーションのプロセスに常駐する。つまり、「どのファイルを開こうが、どのような操作をしようが、常に監視の目が光っている」状態、すなわち「面」の防御を実現できる。

今回の設計における重要なコンポーネントは以下の2点だ。
1. Class Module (Event Listener): `Application`オブジェクトのイベントを捕捉するための「耳」となる。
2. Standard Module (Controller): アドインの起動・終了、およびイベントハンドラの実装を制御する「脳」となる。

2. 実装の核心:イベントハンドラの構築

イベントを捕捉するためには、`WithEvents`キーワードを用いたクラスモジュールの作成が不可欠である。ここでの肝は、`PresentationBeforeSave`イベントの引数である`Cancel`プロパティの制御だ。これを`True`に書き換えることで、PowerPointの標準的な保存プロセスを強制的に中断させることができる。

【コード例】クラスモジュール: `clsAppEventHandler`

このクラスは、アプリケーション全体の動きを監視する「センサー」の役割を果たす。

Option Explicit

' Applicationオブジェクトのイベントを捕捉するための宣言
Private WithEvents App As Application

' アドイン起動時にイベントリスナーを初期化するメソッド
Public Sub Initialize(ByRef targetApp As Application)
    Set App = targetApp
End Sub

' アドイン終了時にメモリを解放するメソッド
Public Sub Terminate()
    Set App = Nothing
End Sub

''' <summary>
''' 保存直前に実行されるイベントハンドラ
''' </summary>
''' <param name="Pres">保存しようとしているプレゼンテーション</param>
''' <param name="Cancel">Trueに設定すると保存をキャンセルする</param>
Private Sub App_PresentationBeforeSave(ByVal Pres As Presentation, Cancel As Boolean)
    On Error GoTo ErrorHandler

    ' --- セキュリティチェックロジックの開始 ---
    ' 本来は外部のAPIやDB、あるいは特定のポリシーファイルを参照すべきだが、
    ' ここではデモンストレーションとして「機密キーワード」の有無を判定する。
    
    If IsSecurityViolation(Pres) Then
        ' 違反が確認された場合、ユーザーに警告を表示
        MsgBox "【セキュリティ警告】" & vbCrLf & _
               "この資料には承認されていない機密情報(キーワード: 'CONFIDENTIAL')が含まれているか、" & _
               "セキュリティ基準を満たしていません。" & vbCrLf & _
               "保存を中止します。", vbCritical, "Policy Violation"
        
        ' 保存プロセスを強制キャンセルする
        Cancel = True
    End If
    ' --- セキュリティチェックロジックの終了 ---

Exit Sub

ErrorHandler:
    ' 致命的なエラーが発生した場合、セキュリティの「穴」を作らないよう、
    ' 安全側に倒して保存をキャンセルする(Fail-Safe設計)
    Cancel = True
    MsgBox "セキュリティスキャンの実行中にエラーが発生しました。安全のため保存をブロックしました。" & vbCrLf & _
           "Error: " & Err.Description, vbCritical, "System Error"
End Sub

''' <summary>
''' セキュリティポリシーに違反しているか判定する内部関数
''' </summary>
Private Function IsSecurityViolation(ByVal Pres As Presentation) As Boolean
    Dim sld As Slide
    Dim shp As Shape
    Dim forbiddenWord As String
    
    forbiddenWord = "CONFIDENTIAL" ' 監視対象のキーワード
    IsSecurityViolation = False

    ' 全スライドを走査(パフォーマンスに注意が必要な領域)
    For Each sld In Pres.Slides
        For Each shp In sld.Shapes
            ' テキストボックスや図形内のテキストをチェック
            If shp.HasTextFrame Then
                If shp.TextFrame.HasText Then
                    If InStr(1, shp.TextFrame.TextRange.Text, forbiddenWord, vbTextCompare) > 0 Then
                        IsSecurityViolation = True
                        Exit Function
                    End If
                End If
            End If
        Next shp
    Next sld
End Function

【コード例】標準モジュール: `modMain`

アドインのライフサイクル(起動・停止)を管理するエントリポイントだ。

Option Explicit

' クラスインスタンスを保持するためのグローバル変数
' これがスコープから外れると、イベント監視が停止してしまうため、極めて重要
Private GlobalHandler As clsAppEventHandler

''' <summary>
''' アドインの起動時に呼び出される
''' </summary>
Public Sub Auto_Open()
    ' インスタンスの生成
    Set GlobalHandler = New clsAppEventHandler
    
    ' Applicationオブジェクトをクラスに渡して監視を開始
    GlobalHandler.Initialize Application
    
    ' デバッグ・運用確認用
    ' Debug.Print "Security Add-in Loaded: " & Now
End Sub

''' <summary>
''' アドインの停止時に呼び出される
''' </summary>
Public Sub Auto_Close()
    If Not GlobalHandler Is Nothing Then
        GlobalHandler.Terminate
        Set GlobalHandler = Nothing
    End If
End Sub

3. プロフェッショナルが意識すべき「堅牢な設計」の3原則

コードを書くだけでは、エンジニアではない。アーキテクトになれ。以下の3点は、実務でツールを運用する際に必ず直面する壁だ。

① Fail-Safe(フェイルセーフ)の原則

セキュリティツールにおいて最も恐ろしいのは、「チェック機能のバグによって、本来止めるべき保存が通ってしまうこと」である。
上記のコードの `ErrorHandler` を見てほしい。エラーが発生した際に `Cancel = True` としている。これは「エラーが起きたら、安全のために保存をさせない」という設計思想だ。利便性を犠牲にしてでも、セキュリティを優先する。これがプロの判断である。

② パフォーマンスの重み(Complexity Analysis)

`PresentationBeforeSave` は、ユーザーが保存ボタンを押すたびに実行される。もしスライドが数百枚あり、その中の全シェイプを再帰的にスキャンしていれば、保存動作が著しく重くなり、ユーザーの不満は爆発する。
実務では、以下の最適化を検討せよ。

  • 差分チェック: 前回の保存時から変更があったスライドのみを対象とする。
  • インデックス化: 特定のタグ(CustomDocumentProperties)が付与されている場合のみ詳細スキャンを行う。
  • 非同期処理の検討: VBA単体では限界があるが、重い処理を外部プロセス(PythonやC#のDLL)に逃がす設計も視野に入れる。

③ 外部連携の疎結合化

「機密キーワード」をVBAコード内にハードコードするのは、三流の仕事だ。
キーワードリストやポリシー設定は、外部のJSONファイル、設定用DB、または社内のAPIから取得するように設計せよ。これにより、VBAコードを書き換えてアドインを再配布することなく、ポリシーの変更(キーワードの追加など)を即座に全ユーザーへ適用できる。

4. 結論:ツールは「守り」の要である

今回紹介した手法は、単なる自動化のテクニックではない。組織の資産を守るための「統制(Governance)」の仕組み化である。

アドインとして実装し、オブジェクトのライフサイクルを正しく管理し、エラー時には安全側に倒す。この一連の設計思想を理解して初めて、あなたは「動くコード」を書くプログラマーから、「信頼されるシステム」を構築するエンジニアへと進化できる。

さあ、このコードをベースに、君の組織に最適な最強の防壁を構築してほしい。

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