【実務・中級編】【中級者向け】受信メールのヘッダー情報を解析し、なりすましメールの疑いがある場合に隔離フォルダへ移動するセキュリティツール – Outlook VBA解析バイブル

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

【極限の知見】Outlook VBAによるヘッダー解析型セキュリティエンジンの設計思想

「Outlookの標準ルールで振り分ければ十分だ」――もし君がそう考えているなら、その考えはプロフェッショナルとしてはあまりに甘い。

標準のルール機能は、メールの「表示上の差出人(From)」を見て判断する。しかし、巧妙ななりすましメール(Spoofing)は、このFromヘッダーを偽装する。攻撃者が本当に操作しているのは、メールの伝送経路を示す「Receivedヘッダー」や、認証結果を示す「Authentication-Results」だ。

本記事では、単なる「自動振り分け」の次元を超え、メールのヘッダー情報を深層解析し、不審なメールを隔離する「堅牢なセキュリティ・エンジン」をVBAで構築するための設計思想と、プロダクションレベルのコードを伝授する。

1. なぜ君の書いた自動化ツールは「壊れる」のか

多くの開発者が陥る罠がある。それは、「イベントの発生タイミング」と「オブジェクトのライフサイクル」への無理解だ。

罠1:`Items.Add` イベントの乱用

受信メールを監視するために `Items.Add` イベントを使う者がいるが、これは非効率の極みだ。大量のメールが短時間に流入した際、イベントのキューが溢れ、処理の遅延やOutlookのフリーズを招く。

罠2:`MailItem` オブジェクトの保持

解析のために `MailItem` オブジェクトをグローバル変数などで長く保持しすぎる設計は、メモリリークの原因となる。Outlookはシングルスレッドに近い挙動を示すことがあり、オブジェクトの解放が遅れると、後続のメール処理がブロックされる。

罠3:エラーハンドリングの欠如

「隔離フォルダが存在しない」「ネットワーク遅延でプロパティが取得できない」といった、実務で必ず遭遇する例外を無視したコードは、一度の失敗で全自動化プロセスを停止させる。

2. プロフェッショナルが採用するアーキテクチャ

堅牢なエンジンを作るためには、以下の3層構造で設計すべきだ。

1. Event Listener層: `Application_NewMailEx` を利用し、`EntryID` だけを即座に受け取る。
2. Analysis Engine層: `EntryID` を元にオブジェクトを再取得し、正規表現(RegExp)を用いてヘッダーを解析する。
3. Action Layer層: 解析結果に基づき、フォルダ移動やログ記録を行う。

ここで重要なのは、`NewMailEx` を使うことだ。このイベントは、新着メールの `EntryID` を引数として渡してくれる。これを利用することで、オブジェクトを直接扱うリスクを回避し、確実に「そのメール」を特定できる。

3. 実装:なりすまし検知セキュリティエンジン

以下のコードは、実務への投入を想定した設計となっている。
事前準備: VBAエディタの `Tools` -> `References` から 「Microsoft VBScript Regular Expressions 5.5」 にチェックを入れておけ。これがないと、高度な文字列解析は不可能だ。

'==============================================================================
' Module: SecurityEngine
' Description: 受信メールのヘッダーを解析し、なりすましを検知して隔離する
' Author: Chief Architect
'==============================================================================

Option Explicit

' 設定値(本来は外部ファイルやレジストリから読み込むべきだが、ここでは定数で定義)
Private Const TARGET_FOLDER_NAME As String = "【隔離フォルダ】"
Private Const LOG_FILE_PATH As String = "C:\Logs\MailSecurityLog.txt" ' 適切なパスに変更せよ

''' <summary>
''' Outlookイベントハンドラ: 新着メール受信時にトリガーされる
''' </summary>
''' <param name="EntryID">受信したアイテムの一意のID</param>
Public Sub Application_NewMailEx(ByVal EntryID As String)
    Dim objItem As Object
    Dim mailItem As Outlook.MailItem
    
    On Error GoTo ErrorHandler

    ' 1. EntryIDからオブジェクトを確実に取得(ライフサイクル管理)
    Set objItem = Application.Session.GetItemFromID(EntryID)
    
    ' MailItemであることを確認
    If TypeOf objItem Is Outlook.MailItem Then
        Set mailItem = objItem
        
        ' 2. 解析エンジンへの受け渡し
        If IsSuspiciousMail(mailItem) Then
            MoveToIsolationFolder mailItem
        End If
    End If

CleanUp:
    ' オブジェクトの明示的な解放
    Set mailItem = Nothing
    Set objItem = Nothing
    Exit Sub

ErrorHandler:
    ' 致命的なエラーをログに記録し、システム全体を止めない
    WriteLog "CRITICAL ERROR: " & Err.Description & " (ID: " & EntryID & ")"
    Resume CleanUp
End Sub

''' <summary>
''' ヘッダー解析エンジン: 正規表現を用いてなりすましを判定する
''' </summary>
Private Function IsSuspiciousMail(ByRef mail As Outlook.MailItem) As Boolean
    Dim strHeaders As String
    Dim regEx As New RegExp
    Dim isFraud As Boolean
    
    isFraud = False
    ' 全てのヘッダー情報を取得
    strHeaders = mail.PropertyAccessor.GetProperty("http://schemas.microsoft.com/mapi/proptag/0x007D001E") ' PR_TRANSPORT_MESSAGE_HEADERS
    
    ' --- 解析ロジック 1: Authentication-Results のチェック ---
    ' SPFやDKIMがfailしている場合、極めて疑わしい
    regEx.Pattern = "Authentication-Results:.*?(spf|dkim|dmarc)=fail"
    regEx.IgnoreCase = True
    If regEx.Test(strHeaders) Then
        isFraud = True
        WriteLog "SUSPICIOUS: Authentication Failed for " & mail.SenderEmailAddress
    End If
    
    ' --- 解析ロジック 2: Receivedヘッダーの不整合チェック ---
    ' 特定の信頼できるドメイン(例: 自社ドメイン)を名乗っているが、
    ' ReceivedヘッダーにそのドメインのIP/ホストが含まれていない等のパターンをここに実装
    ' 今回は簡易的に、特定の不審なキーワードが含まれる場合を例示
    regEx.Pattern = "Received: from (?!trust-server\.com)" ' trust-server.com 以外からの受信を検知(例)
    ' ※実運用ではホワイトリスト形式で定義せよ
    
    If Not isFraud And regEx.Test(strHeaders) Then
        ' ここに詳細なロジックを組み込む
    End If

    IsSuspiciousMail = isFraud
End Function

''' <summary>
''' アクション層: アイテムを隔離フォルダへ移動する
''' </summary>
Private Sub MoveToIsolationFolder(ByRef mail As Outlook.MailItem)
    Dim targetFolder As Outlook.Folder
    Dim ns As Outlook.NameSpace
    
    On Error GoTo MoveError
    
    Set ns = Application.GetNamespace("MAPI")
    
    ' 隔離フォルダの存在確認と取得
    On Error Resume Next
    Set targetFolder = ns.GetDefaultFolder(olFolderInbox).Parent.Folders(TARGET_FOLDER_NAME)
    On Error GoTo MoveError
    
    ' フォルダが存在しない場合は作成(実務では作成権限に注意)
    If targetFolder Is Nothing Then
        Set targetFolder = ns.GetDefaultFolder(olFolderInbox).Parent.Folders.Add(TARGET_FOLDER_NAME)
        WriteLog "INFO: Created Isolation Folder: " & TARGET_FOLDER_NAME
    End If
    
    ' 移動実行
    mail.Move targetFolder
    WriteLog "ACTION: Moved suspicious mail to isolation: " & mail.Subject & " (" & mail.SenderEmailAddress & ")"
    
    Exit Sub

MoveError:
    WriteLog "ERROR: Failed to move mail: " & Err.Description
End Sub

''' <summary>
''' ユーティリティ: ログ出力
''' </summary>
Private Sub WriteLog(ByVal message As String)
    Dim fileNum As Integer
    fileNum = FreeFile
    
    On Error Resume Next ' ログ書き込み失敗でメイン処理を止めない
    Open LOG_FILE_PATH For Append As #fileNum
    Print #fileNum, Now & " | " & message
    Close #fileNum
End Sub

4. プロダクション運用のための極意

このコードを現場にデプロイする際、以下の3点を必ず考慮せよ。

① 外部設定の分離

上記のコードでは `TARGET_FOLDER_NAME` などを定数としているが、実務ではこれらを CSVファイルやJSONファイル、あるいはExcelの隠しシート から読み込むように設計変更せよ。コードを書き換えることなく、ホワイトリストやブラックリストを更新できる柔軟性が、保守性を決定づける。

② パフォーマンスのトレードオフ

`PropertyAccessor` を使用してヘッダーを取得する操作は、通常のプロパティ参照よりもコストが高い。すべてのメールに対して重い正規表現をかけるのではなく、まずは `mail.SenderEmailAddress` 等で一次フィルタリングを行い、「怪しい可能性があるものだけ」をディープスキャンする という二段構えの設計にせよ。

③ ログの重要性

セキュリティツールにおいて、ログは「証拠」だ。単にエラーを記録するだけでなく、「なぜこのメールを隔離したのか(どのヘッダーパターンに合致したのか)」という判定理由をログに残すように拡張せよ。これがなければ、誤検知(False Positive)が発生した際に、原因究明ができず、ツール自体が廃棄されることになる。

結論

VBAは、単なるマクロ言語ではない。設計思想次第で、強力なセキュリティ・エージェントへと昇華させることができる。
「動けばいい」という思考を捨て、「例外に強く、リソースに優しく、根拠に基づいた判断を下す」 コードを書け。それが、エンジニアとしての格の違いを見せつける唯一の方法だ。

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