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

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

「SenderEmailAddress」を信じるな。Outlook VBAで構築する、ヘッダー解析による堅牢ななりすまし検知エンジン

業務自動化の現場において、多くのエンジニアが陥る致命的な罠がある。それは、「表示されている送信元アドレスを、真実だと過信すること」だ。

「送信元が `ceo@yourcompany.com` になっているから安全だ」――そんな思考停止したロジックで書かれたコードは、高度ななりすまし攻撃(Spoofing)の前では、ただの無力なスクリプトに過ぎない。

本記事では、単なる「振り分けツール」の次元を超え、メールのメタデータ、すなわちインターネット・メッセージ・ヘッダーを直接解析することで、認証情報(SPF/DKIM/DMARC)の成否を判定し、リスクのあるメールを即座に隔離する「防御型自動化エンジン」の設計思想と実装を伝授する。

1. 設計思想:なぜ「既存のプロパティ」では不十分なのか

多くの初級者が書くコードは、`MailItem.SenderEmailAddress` をチェックするものだ。しかし、攻撃者はメールの「表示名」や「Envelope From」を容易に偽装できる。

プロフェッショナルが狙うべきは、メールの「真実」が刻印されているInternet Message Headersである。現代のメールセキュリティにおいて、その真実とは以下の3点に集約される。

1. SPF (Sender Policy Framework): 送信元IPアドレスが、ドメインの許可リストにあるか。
2. DKIM (DomainKeys Identified Mail): メールの内容が、公開鍵によって正当に署名されているか。
3. DMARC: 上記の認証が失敗した際、ドメイン所有者がどう振る舞うよう指示しているか。

これらはメールヘッダー内の `Authentication-Results` フィールドに集約されている。我々が構築すべきは、このフィールドを読み取り、`spf=pass` や `dkim=pass` といった文字列をスキャンするロジックである。

2. アーキテクチャ:堅牢性を担保する3つの原則

プロダクション環境で動かすコードには、以下の3原則が不可欠だ。

① `NewMailEx` イベントの採用

`NewMail` イベントは、複数のメールが同時に着信した際に、イベントが間引かれたり、どのメールに対して処理すべきかを見失う(Race Condition)リスクがある。
一方、`NewMailEx(EntryID)` は、着信した各メールの固有識別子(EntryID)を引数として渡す。「EntryIDを起点にオブジェクトを取得する」。これが、バグを寄せ付けない鉄則だ。

② PropertyAccessorによる低レイヤーへのアクセス

`MailItem` の標準プロパティには、ヘッダーの全容は格納されていない。MAPIプロパティのスキーマ(`http://schemas.microsoft.com/mapi/proptag/0x007D001E`)を叩き、生のヘッダー文字列を取得する必要がある。

③ 失敗を前提としたエラーハンドリング

メールには、暗号化されたもの、特殊なエンコードが施されたもの、あるいは破損しているものが存在する。これらを無視して処理を続行すれば、VBAは即座にクラッシュする。「解析に失敗した場合は、安全側に倒して隔離する」。これがセキュリティエンジニアの思考だ。

3. 実装:なりすまし検知・隔離エンジンのソースコード

以下に、実務に投入可能なレベルのコード例を示す。このコードは、`ThisOutlookSession` モジュールに記述することを想定している。

' ======================================================================================
' Module: ThisOutlookSession
' Description: 高度なヘッダー解析によるなりすましメール検知・隔離エンジン
' Author: Chief Architect
' ======================================================================================

Option Explicit

' MAPIプロパティタグ: Internet Message Headers
Private Const PR_INTERNET_MESSAGE_HEADERS As String = "http://schemas.microsoft.com/mapi/proptag/0x007D001E"
' 隔離用フォルダ名
Private Const ISOLATION_FOLDER_NAME As String = "【隔離】なりすまし疑い"

''' <summary>
''' 受信イベントハンドラ
''' </summary>
''' <param name="EntryID">着信したアイテムの固有識別子</param>
Private Sub Application_NewMailEx(ByVal EntryID As String)
    Dim objItem As Object
    Dim objMail As Outlook.MailItem
    
    On Error GoTo ErrorHandler
    
    ' EntryIDからアイテムを直接取得(Race Conditionを回避する設計)
    Set objItem = Application.Session.GetItemFromID(EntryID)
    
    ' メールアイテム以外(会議出席依頼など)は無視
    If Not TypeOf objItem Is Outlook.MailItem Then Exit Sub
    Set objMail = objItem
    
    ' 1. ヘッダー解析によるリスク判定
    If IsSpoofingDetected(objMail) Then
        ' 2. リスクが確認された場合、隔離処理を実行
        MoveToIsolationFolder objMail
    End If

    Exit Sub

ErrorHandler:
    ' 本番環境では、エラーログをファイルやDBに書き出す設計を推奨
    Debug.Print "Critical Error in NewMailEx: " & Err.Description
End Sub

''' <summary>
''' ヘッダー内のAuthentication-Resultsを解析し、なりすましの疑いがあるか判定する
''' </summary>
''' <param name="mail">解析対象のメールアイテム</param>
''' <returns>なりすましの疑いがある場合は True</returns>
Private Function IsSpoofingDetected(ByVal mail As Outlook.MailItem) As Boolean
    Dim strHeaders As String
    Dim isSafe As Boolean
    
    ' 初期値は「安全ではない」とする(Fail-Safe設計)
    IsSpoofingDetected = True
    
    On Error GoTo HeaderError
    
    ' PropertyAccessorを使用して生のヘッダーを取得
    strHeaders = mail.PropertyAccessor.GetProperty(PR_INTERNET_MESSAGE_HEADERS)
    
    ' 判定ロジック: Authentication-Results 内の spf, dkim, dmarc の成否を確認
    ' 少なくとも一つでも "pass" が含まれていれば、暫定的に安全とみなす(要件に応じて調整)
    ' ※ 実際には、送信ドメインとの一致確認などの高度なロジックが必要
    If InStr(1, strHeaders, "spf=pass", vbTextCompare) > 0 And _
       InStr(1, strHeaders, "dkim=pass", vbTextCompare) > 0 And _
       InStr(1, strHeaders, "dmarc=pass", vbTextCompare) > 0 Then
        isSafe = True
    Else
        isSafe = False
    End If
    
    ' 安全な場合は False を返す
    If isSafe Then IsSpoofingDetected = False
    Exit Function

HeaderError:
    ' ヘッダーが取得できない、あるいは解析不能な場合は「疑わしい」として扱う
    Debug.Print "Header Analysis Failed for: " & mail.Subject
    IsSpoofingDetected = True
End Function

''' <summary>
''' 指定したメールを隔離フォルダへ移動する
''' </summary>
''' <param name="mail">移動対象のメール</param>
Private Sub MoveToIsolationFolder(ByVal mail As Outlook.MailItem)
    Dim targetFolder As Outlook.Folder
    Dim ns As Outlook.NameSpace
    
    On Error GoTo MoveError
    Set ns = mail.GetStore
    
    ' 隔離フォルダの存在確認と取得
    Set targetFolder = GetOrCreateFolder(ns.GetDefaultFolder(olFolderInbox), ISOLATION_FOLDER_NAME)
    
    ' 移動実行
    mail.Move targetFolder
    Debug.Print "Mail Isolated: " & mail.Subject
    Exit Sub

MoveError:
    Debug.Print "Failed to move mail: " & Err.Description
End Sub

''' <summary>
''' フォルダの存在を確認し、なければ作成する(堅牢なフォルダ管理)
''' </summary>
Private Function GetOrCreateFolder(ByVal parentFolder As Outlook.Folder, ByVal folderName As String) As Outlook.Folder
    Dim subFolder As Outlook.Folder
    
    On Error Resume Next
    Set subFolder = parentFolder.Folders(folderName)
    On Error GoTo 0
    
    If subFolder Is Nothing Then
        Set subFolder = parentFolder.Folders.Add(folderName)
    End If
    
    Set GetOrCreateFolder = subFolder
End Function

4. プロフェッショナルのための補足:運用における注意点

このツールを実務に投入する際、以下の3点に留意せよ。

データベース連携の検討

もし、あなたが「特定のドメインからのなりすましをブラックリスト化したい」と考えるなら、VBA内にハードコードしてはいけない。外部のCSVファイル、あるいはSQLiteなどの軽量データベースを参照する設計にせよ。これにより、コードを書き換えることなく、セキュリティポリシーの更新が可能になる。

誤検知(False Positive)への対策

セキュリティを強めすぎると、正当なメールまで隔離されてしまう。

  • ホワイトリストの実装: 社内ドメインや、信頼できるパートナー企業のドメインは、ヘッダー解析をスキップするロジックを必ず組み込むこと。
  • 隔離通知: 隔離した事実は、管理者にログとして残すか、あるいはユーザーに「怪しいメールを隔離しました」と通知する仕組みを検討せよ。

パフォーマンスの重み

`PropertyAccessor` は強力だが、大量のメールを一度に処理しようとするとオーバーヘッドが発生する。`NewMailEx` を使い、着信の都度、ピンポイントで処理を行う今の設計は、パフォーマンスの観点からも理にかなっている。

結論

「動けばいい」というコードは、現場では「負債」でしかない。
真に価値のある自動化とは、「例外的な事態を予測し、それに対する振る舞いを設計に組み込んでいるもの」だ。

今回紹介したヘッダー解析のアプローチは、その第一歩に過ぎない。しかし、この「メタデータを疑い、真実を掴みに行く」という姿勢こそが、エンジニアとしての格の違いを決めるのである。

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