【Outlook VBA】送信ボタンを押した瞬間、社外アドレスを看破せよ。誤送信を防ぐ「ドメイン監視フック」の極意
業務自動化エンジニアの私のもとに、今日もこんな悲痛な相談が舞い込む。
> 「クライアント宛の機密データを、誤って同名の別会社に送ってしまいました……。どうにか再発を防げませんか?」
ヒューマンエラーを「気をつけてね」の一言で解決しようとするのは、エンジニアリングの放棄と同義だ。人間は必ずミスをする生き物である。だからこそ、「システム側で強制的に検知し、立ち止まらせる仕組み」を構築しなければならない。
今回は、Outlook VBAのイベントフックを駆使し、メール送信直前に宛先ドメインをスキャン、社外アドレスが混入していた場合に警告を発して送信を阻止する「堅牢な入力チェック機構」の実装方法を伝授する。
—
なぜ「送信時(ItemSend)」のフックが必要なのか?
初心者がやりがちな間違いは、「メール作成画面を開いた瞬間(Open)」や「宛先を入力した瞬間」にチェックを入れようとすることだ。しかし、これではリアルタイムな宛先の追加・変更に対応しきれなかったり、無駄なイベント発火でOutlookのパフォーマンスを重くしたりする。
我々が狙うべきは、ユーザーが「送信」ボタンを押したその瞬間だ。
Outlookオブジェクトモデルが提供する `Application_ItemSend` イベントを捕捉すれば、メールがSMTPサーバーへ旅立つコンマ数秒前に、内容を完全に掌握・制御できる。
—
バグを生まないための設計思想
プロダクション環境(実務)でVBAを動かす場合、以下の要件を満たさなければ「使い物にならないゴミコード」になってしまう。
1. 大文字小文字の揺れを無視する(例: `@Example.com` と `@example.COM` は同一視する)
2. 社内ドメインをハードコーディングせず、定数または外部設定で柔軟に管理する
3. 例外(メーリングリストや特定の共有アドレスなど)を考慮する
4. ユーザーが「誤送信に気づいてキャンセル」した際、確実に送信処理を中断させる
これらをすべて満たすスマートなコードを次項で提示する。
—
実装コード:`ThisOutlookSession` の全貌
Outlook VBAにおいて、イベントを受け取るためには専用の特等席がある。それが `ThisOutlookSession` だ。
以下のコードを、OutlookのVBAエディタ(`Alt + F11` で起動)の `ThisOutlookSession` モジュールにそのまま貼り付けてほしい。
Option Explicit
‘ ==============================================================================
‘ 局所定数定義
‘ ==============================================================================
‘ 自社のメールドメイン(複数ある場合はカンマ区切りで判定ロジックを拡張可能)
Private Const INTERNAL_DOMAIN As String = “example.co.jp”
‘ ==============================================================================
‘ イベントプロシージャ: メールの送信ボタンが押された瞬間に強制発動
‘ ==============================================================================
Private Sub Application_ItemSend(ByVal Item As Object, Cancel As Boolean)
On Error GoTo ErrorHandler
‘ 対象がMailItem(通常のメール)以外(会議通知やタスクなど)の場合はスルー
If TypeName(Item) <> “MailItem” Then Exit Sub
Dim mail As MailItem
Set mail = Item
‘ 宛先(To, CC, BCC)に社外アドレスが含まれているかスキャン
If HasExternalRecipient(mail, INTERNAL_DOMAIN) Then
‘ 警告メッセージボックスを表示(はい / いいえ)
Dim response As VbMsgBoxResult
response = MsgBox(“【警告】社外のアドレス(または指定外ドメイン)が含まれています!” & vbCrLf & _
“本当にこのまま送信してもよろしいですか?”, _
vbExclamation + vbYesNo + vbDefaultButton2, _
“誤送信防止アラート”)
‘ 「いいえ」が選ばれた場合、送信をキャンセルする
If response = vbNo Then
Cancel = True
‘ ユーザーが再編集できるようにインスペクター(作成画面)をアクティブ化
mail.Display
Exit Sub
End If
End If
Exit Sub
ErrorHandler:
MsgBox “ドメインチェック処理中に予期せぬエラーが発生しました。” & vbCrLf & _
“エラー番号: ” & Err.Number & vbCrLf & _
“エラー内容: ” & Err.Description, vbCritical, “システムエラー”
‘ 安全のため、異常発生時は誤送信を防ぐために送信をキャンセルする設計思想もあり
‘ Cancel = True
End Sub
‘ ==============================================================================
‘ 内部関数: すべての受信者(To/CC/BCC)を走査し、社外ドメインを検知する
‘ ==============================================================================
Private Function HasExternalRecipient(ByVal mail As MailItem, ByVal targetDomain As String) As Boolean
Dim recip As Recipient
Dim emailAddress As String
Dim domainPart As String
Dim atPos As Long
HasExternalRecipient = False ‘ 初期値は偽(社外なし)
‘ Recipientsコレクションを完全走査
For Each recip In mail.Recipients
‘ Exchangeの内部アドレス(EXトランスポート)等の特殊な形式を考慮
‘ SMTPアドレスを強制的に取得する
If recip.AddressEntry.AddressEntryUserType = olExchangeUserAddressEntry Or _
recip.AddressEntry.AddressEntryUserType = olExchangeRemoteUserAddressEntry Then
emailAddress = recip.AddressEntry.GetExchangeUser().PrimarySmtpAddress
Else
emailAddress = recip.Address.Address
End If
‘ “@”の位置を特定してドメイン部分を抽出
atPos = InStr(emailAddress, “@”)
If atPos > 0 Then
domainPart = LCase$(Mid$(emailAddress, atPos + 1))
‘ 自社ドメインと一致しないものを「社外」と判定
‘ ※厳密に完全一致させる(部分一致によるバイパスを防ぐ)
If domainPart <> LCase$(targetDomain) Then
HasExternalRecipient = True
Exit Function ‘ 1件でも見つかったら即座に抜ける(高速化)
End If
End If
Next recip
End Function
—
コードの急所:プロが仕込んだ3つのこだわり
ただ動くだけのコードなら素人でも書ける。ここではチーフアーキテクトとしてのこだわりを3点解説しよう。
1. `Recipient.Address` の罠と `PrimarySmtpAddress` の回避策
社内Exchange環境において、宛先に社内メンバーを選ぶと、メールアドレスではなく `/o=First Organization/ou=…` のような内部識別子(EX形式)が返ってくることがある。これをそのまま `@` でスライスしようとすると、バグるか誤判定を起こす。
コード内にある通り、`AddressEntryUserType` を判定し、確実に SMTPプライマリ・アドレス を引くロジックを入れている点が、実務で耐えうる所以だ。
2. 大文字小文字の正規化 (`LCase$`)
人間は `Example.CO.JP` のように気まぐれに入力する。文字列比較をそのまま行うと、システムは「別ドメインだ」と勘違いして過剰検知する。`LCase$` 関数で小文字に統一してから比較することで、無用なトラブルを完全に排除している。
3. `Cancel = True` とインスペクターの制御
ユーザーが「いいえ」を選択して送信を中止した際、単に `Cancel = True` にするだけだと、メール作成画面が裏に隠れてしまったり操作不能に陥ることがある。そのため、明示的に `mail.Display` を呼び出し、ユーザーが即座に宛先を修正できるようにハンドリングしている。
—
運用上の注意点とさらなる高みへ
このスクリプトを導入するだけで、組織全体の誤送信インシデントは劇的に減少する。しかし、システム管理者・開発者として次のステップも視野に入れておいてほしい。
- 外部設定ファイル化:
今回は定数 `INTERNAL_DOMAIN` でハードコーディングしているが、子会社が増えたりドメインが追加されたりするたびにVBAを書き換えるのは非効率だ。テキストファイル(INIやJSON)やExcelマスタから動的にドメインリストを読み込む設計に拡張すると、メンテナンス性がさらに向上する。
- セキュリティポリシー(デジタル署名):
マクロのセキュリティ設定を「すべてのマクロを有効にする」にすることは組織として御法度だ。実務展開する際は、自己署名証明書を作成してVBAプロジェクトに署名するか、グループポリシー(GPO)で信頼済み場所を指定する必要がある。
実務の信頼性を担保するのは、こうした細部へのこだわりと、エラーを想定し尽くした堅牢な設計だ。ぜひあなたの現場でもこのフックを導入し、誤送信という悪夢を根絶してほしい。
