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

スポンサーリンク

【Outlook VBA極限解説】受信メールヘッダー解析による「なりすまし」自動隔離エンジンの構築

シニアエンジニアおよび社内システム管理者の各位。
日々の業務において、巧妙化するフィッシングメールやBEC(ビジネスメール詐欺)の波状攻撃に辟易していることだろう。市価の高価なセキュリティゲートウェイは当然として、エンドポイントであるOutlook側でも「最後の砦」として自衛するアーキテクチャが求められている。

今回は、Outlook VBAのイベント駆動モデルを極限までチューニングし、受信メールのインターネットヘッダーを解析、SPF/DKIMの整合性やFromアドレスの偽装(Display Name Spoofing)を検知して瞬時に隔離フォルダへ自動移動させる「セキュリティ監視・隔離ツール」の実装を解説する。

表面的なコードの寄せ集めではない。オブジェクトのライフサイクル、メモリ管理、そしてCOMの重みを知り尽くした者だけが到達できる「現場で使える極限の知見」を授けよう。

—

1. アーキテクチャの要件とOutlook VBAの限界突破

Outlook VBAでセキュアなメール処理を実装する際、最大のボトルネックとなるのは「処理の重さとイベントの取りこぼし」である。
`NewMailEx` イベントは高速だが、受信トレイ以外のフォルダへの着信や、大量メール受信時のスレッドブロックを引き起こす。さらに、OutlookのDOM(Document Object Model)はメモリリークを起こしやすいことで悪名高い。

本アーキテクチャでは以下の鉄則を遵守する。

1. `PropertyAccessor` による高速ヘッダー抽出:`MailItem.Body` をパースする愚行は避け、MAPIプロパティ(PR_TRANSPORT_MESSAGE_HEADERS)へ直接アクセスする。
2. 完全なオブジェクトの明示的解放:`Set obj = Nothing` の徹底により、Outlookプロセスのメモリ肥大化を防ぐ。
3. 安全なエラーバリア:イベントハンドラー内での予期せぬエラーがOutlook全体をクラッシュさせないよう、厳格なエラーハンドリングを実装する。

—

2. 実装コード:セキュリティ監視・自動隔離エンジン

以下のコードを、Outlookの `ThisOutlookSession` モジュールに配置する。
※事前にメールボックス内に「_Quarantine(隔離用)」という名称のフォルダを手動で作成しておくこと。

Option Explicit

‘ ==============================================================================
‘ 局所定数定義
‘ ==============================================================================
Private Const PR_TRANSPORT_MESSAGE_HEADERS As String = “http://schemas.microsoft.com/mapi/proptag/0x007D001E”
Private Const QUARANTINE_FOLDER_NAME As String = “_Quarantine”

‘ ==============================================================================
‘ イベント: 新着メール検知 (NewMailExはItemIDCollectionを渡すため高速)
‘ ==============================================================================
Private Sub Application_NewMailEx(ByVal EntryIDCollection As String)
On Error GoTo ErrorHandler

Dim vEntryIDs As Variant
Dim i As Long
Dim ns As Outlook.NameSpace
Dim objItem As Object

‘ 複数同時受信時はカンマ区切りで渡されるため分解する
vEntryIDs = Split(EntryIDCollection, “,”)
Set ns = Application.GetNamespace(“MAPI”)

For i = LBound(vEntryIDs) To UBound(vEntryIDs)
‘ セッションを跨ぐためGetItemFromIDを使用
Set objItem = ns.GetItemFromID(vEntryIDs(i))

‘ MailItemであるかを型安全に判定
If TypeOf objItem Is Outlook.MailItem Then
Call ProcessSecurityInspection(objItem)
End If

‘ ループ内でのオブジェクト解放の鉄則
Set objItem = Nothing
Next i

CleanUp:
Set ns = Nothing
Exit Sub

ErrorHandler:
‘ 冗長なMsgBoxはサーバーサイド/常時稼働環境では御法度。Debug.Printまたはログ出力へ
Debug.Print “[CRITICAL] Application_NewMailEx Error: ” & Err.Description
Resume CleanUp
End Sub

‘ ==============================================================================
‘ コアロジック: セキュリティ検査と隔離処理
‘ ==============================================================================
Private Sub ProcessSecurityInspection(ByVal mail As Outlook.MailItem)
Dim oPropAccessor As Outlook.PropertyAccessor
Dim rawHeaders As String
Dim senderEmail As String
Dim displaySender As String
Dim isSuspicious As Boolean

isSuspicious = False

On Error GoTo CleanUp

‘ 1. PropertyAccessorによる極限まで効率的なヘッダー取得
Set oPropAccessor = mail.PropertyAccessor
rawHeaders = oPropAccessor.GetProperty(PR_TRANSPORT_MESSAGE_HEADERS)

‘ 2. 表示名偽装(Display Name Spoofing)の検知ロジック
‘ 例: 表示名が社内重役の名前でありながら、実際のFromアドレスが外部フリーメールの場合
displaySender = mail.SenderName
senderEmail = LCase(mail.SenderEmailAddress)

If IsDisplaynameSpoofed(displaySender, senderEmail) Then
isSuspicious = True
Debug.Print “[ALERT] Display Name Spoofing detected. Display: ” & displaySender & ” / Email: ” & senderEmail
End If

‘ 3. ヘッダー内SPF/DKIM評価結果の簡易パース (Authentication-Results)
If InStr(1, rawHeaders, “spf=fail”, vbTextCompare) > 0 Or _
InStr(1, rawHeaders, “dkim=fail”, vbTextCompare) > 0 Then
isSuspicious = True
Debug.Print “[ALERT] SPF/DKIM validation failed in headers for Subject: ” & mail.Subject
End If

‘ 4. 判定結果に基づく隔離フォルダへの移動
If isSuspicious Then
Call MoveToQuarantine(mail)
End If

CleanUp:
‘ オブジェクトの明示的解放(メモリリークの完全排除)
Set oPropAccessor = Nothing
On Error GoTo 0
End Sub

‘ ==============================================================================
‘ 補助関数: 表示名偽装の判定アルゴリズム
‘ ==============================================================================
Private Function IsDisplaynameSpoofed(ByVal dispName As String, ByVal email As String) As String
‘ 【社内管理者向けカスタマイズポイント】
‘ 自社ドメインの定義
Const INTERNAL_DOMAIN As String = “@your-company.co.jp”

‘ ケースA: 表示名に特定の重要キーワード(例: 代表取締役、総務部長など)が含まれているが、
‘ 送信元アドレスが自社ドメインではない場合
If (InStr(1, dispName, “社長”, vbTextCompare) > 0 Or _
InStr(1, dispName, “CEO”, vbTextCompare) > 0 Or _
InStr(1, dispName, “情報システム”, vbTextCompare) > 0) Then

If Right(email, Len(INTERNAL_DOMAIN)) <> INTERNAL_DOMAIN Then
IsDisplaynameSpoofed = True
Exit Function
End If
End If

IsDisplaynameSpoofed = False
End Function

‘ ==============================================================================
‘ 補助関数: 指定隔離フォルダへの安全な移動
‘ ==============================================================================
Private Sub MoveToQuarantine(ByVal mail As Outlook.MailItem)
Dim ns As Outlook.NameSpace
Dim targetFolder As Outlook.Folder

Set ns = Application.GetNamespace(“MAPI”)
On Error Resume Next
‘ 受信トレイの階層、またはルート直下に作成した隔離フォルダを取得
Set targetFolder = ns.GetDefaultFolder(olFolderInbox).Folders(QUARANTINE_FOLDER_NAME)

If targetFolder Is Nothing Then
‘ フォルダが存在しない場合は受信トレイにフォールバック(あるいは新規作成)
Set targetFolder = ns.GetDefaultFolder(olFolderInbox)
Debug.Print “[WARNING] Quarantine folder not found. Falling back to Inbox.”
End If
On Error GoTo 0

‘ 未読フラグを保持したまま移動
mail.Move targetFolder

Set targetFolder = Nothing
Set ns = Nothing
End Sub

—

3. シニアエンジニアが押さえるべき「極限の知見」

① `PropertyAccessor` と `Body` プロパティの決定的な違い

初心者はメールのヘッダーを読み取る際に `mail.Body` や `mail.HTMLBody` をテキスト検索しがちだが、これは重大なアンチパターンである。
MAPIプロパティである `PR_TRANSPORT_MESSAGE_HEADERS` (`0x007D001E`) を `PropertyAccessor` 経由で取得することで、MAPIストアから直接、余計なHTML解析コストをかけずに生ヘッダー(RFC822形式)をミリ秒単位で引き抜くことができる。

② イベントの多重発火とデッドロックの回避

`NewMailEx` は非同期かつ高速に走るため、処理が重すぎるとOutlook全体のUIスレッドがロックされる。
今回のコードでは、検査対象の判定(`IsDisplaynameSpoofed` および文字列検索)を極限まで軽量化し、疑わしいメールのみ `mail.Move`(COM間通信を伴う重い処理)を実行するように設計している。これによりスループットが劇的に向上する。

③ COMオブジェクトの参照カウントとメモリ最適化

VBAにおける `Set obj = Nothing` は単なる作法ではない。OutlookのCOMラッパーは、ガベージコレクションのタイミングが曖昧であるため、プロシージャを抜ける際に確実に参照カウントをデクリメントしなければ、数日間常時稼働させたOutlookが数GBのメモリを消費してフリーズする原因になる。
特に `PropertyAccessor` や `NameSpace` はスコープが狭くても手動で `Nothing` を代入する強い意志を持て。

—

4. 運用・拡張への提言

このスクリプトはスタンドアロンのクライアントサイド・セキュリティ機構として機能するが、真のシニアエンジニアであれば、これをさらに昇華させるべきだ。

  • ログの外部出力:`Debug.Print` だけでなく、Windowsのイベントビューア(`WScript.Shell` 経由の `EventLog` 書き込み)や、ローカルのCSVファイルへインシデントログを非同期で吐き出す仕組みに拡張する。
  • API連携:疑わしいメールのヘッダー情報を、社内のSIEM基盤(Microsoft SentinelやSplunkなど)へWebAPI(HTTP POST)経由で飛ばすことで、エンドポイントの検知情報を組織全体でインテリジェンスとして共有可能になる。

VBAはもはや「レガシーなオモチャ」ではない。正しくアーキテクチャを理解し、COMの挙動を掌握した者にとっては、OSとアプリケーションの隙間を埋める最も強靭な「超軽量エージェント」となり得るのだ。
君たちの環境でも、今すぐこのコードをデプロイし、システムを防衛せよ。

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