【入門編】【中級者向け】添付ファイルに含まれる「隠し拡張子」を検知して安全に処理するセキュリティ対策 – Outlook VBA解析バイブル

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

【Outlook VBA極限講義】添付ファイルの「隠し拡張子」を見抜き、自動化に「防御力」を実装する

こんにちは。今日もコードと向き合っているあなた、素晴らしいですね。

Outlook VBAで「添付ファイルを自動保存する」という仕組みを作れるようになったなら、あなたはすでに「マクロの記録」という殻を破り、実用的なツールを作れるエンジニアの入り口に立っています。

しかし、ここで一つ、非常に重要な話をしなければなりません。
「自動化は、ミスや攻撃も自動化してしまう」 ということです。

もし、あなたが「拡張子が `.pdf` なら保存する」という単純なロジックでコードを書いていたとしたら……。悪意のある攻撃者が送ってきた `請求書.pdf.exe` というファイルも、あなたのプログラムは「あ、PDFっぽい名前だ」と勘違いして、社内サーバーへ、あるいはあなたのPCへ、ウイルスを自動でダウンロードしてしまうかもしれません。

今回は、中級者へのステップアップとして、「ファイル名の見た目に騙されない、堅牢な拡張子判定ロジック」をマスターしましょう。ここをクリアすれば、あなたの作るツールは「ただ動くもの」から「プロが現場で使えるもの」へと進化します。

1. なぜ「Right関数」だけでは不十分なのか?

初心者の多くは、ファイル名の末尾を判定するために `Right` 関数を使います。

' ⚠️ 初心者が陥りやすい、危険なコードの例
If Right(Attachment.FileName, 4) = ".pdf" Then
    ' 保存処理...
End If

一見、正しく見えますよね? しかし、これには致命的な弱点があります。

「二重拡張子」の罠

攻撃者は、Windowsの設定で「登録されている拡張子は表示しない」となっていることを利用します。
例えば、`important_document.pdf.exe` というファイル。

もし、あなたが「`.pdf` が含まれていたら」という条件(`InStr` など)で判定していたら、この実行ファイル(`.exe`)をPDFとして誤認してしまいます。また、`Right(filename, 4)` で `.pdf` を探しても、`.exe` で終わっていればスルーはされますが、判定ロジックが複雑になると、こうした「末尾の取り違え」はバグの温床になります。

プロの思考: 「ファイル名の末尾が何か」を判定するのではなく、「ドット(.)から後ろの文字列が、許可されたリストに含まれているか」を、厳密に、かつ逆順から解析する必要があります。

2. 解決策:`InStrRev` を使った「真の拡張子」の抽出

ここで登場するのが、VBAの強力な武器 `InStrRev` 関数 です。
`InStr` が「文字列の先頭から」探すのに対し、`InStrRev` は 「文字列の末尾から」 検索を開始します。

ファイル名の中にドットが複数あっても、一番最後にあるドットの位置を特定できれば、それが「真の拡張子」の始まりです。

実装の設計図

1. ファイル名の中から、一番最後に出現するドット `.` の位置を探す。
2. そのドット以降の文字列を「拡張子」として切り出す。
3. その拡張子が、ホワイトリスト(許可リスト) に含まれているか照合する。

3. 実践:堅牢な添付ファイル処理コード

それでは、実際のコードを見ていきましょう。単なるコピペではなく、なぜこのように書くのか、コメントを読み解いてください。

Option Explicit

''' <summary>
''' メイン処理:受信したメールの添付ファイルを安全に保存する
''' </summary>
Sub SafeAttachmentSaver()
    Dim outlookApp As Outlook.Application
    Dim ns As Outlook.NameSpace
    Dim folder As Outlook.MAPIFolder
    Dim mail As Outlook.MailItem
    Dim att As Outlook.Attachment
    Dim savePath As String
    
    ' 保存先フォルダ(あらかじめ作成しておいてください)
    savePath = "C:\Users\Public\Documents\SafeDownloads\"
    
    Set outlookApp = New Outlook.Application
    Set ns = outlookApp.GetNamespace("MAPI")
    ' 受信トレイを選択
    Set folder = ns.GetDefaultFolder(olFolderInbox)
    
    ' フォルダ内のメールをループ(ここでは最新の1件を例にします)
    ' 実際には条件に合わせてループ処理を構築してください
    For Each mail In folder.Items
        If mail.Class = olMail Then
            Debug.Print "Checking Mail: " & mail.Subject
            
            ' 添付ファイルのループ
            For Each att In mail.Attachments
                ' 【重要】ここで独自の安全判定関数を呼び出す
                If IsSafeExtension(att.FileName) Then
                    ' 安全だと判断された場合のみ保存
                    att.SaveAsFile savePath & att.FileName
                    Debug.Print "  [SUCCESS] Saved: " & att.FileName
                Else
                    ' 不審なファイルは警告(または無視)
                    Debug.Print "  [WARNING] Blocked suspicious file: " & att.FileName
                End If
            Next att
        End If
    Next mail
    
    MsgBox "処理が完了しました。", vbInformation
End Sub

''' <summary>
''' 拡張子を解析し、許可された拡張子かどうかを判定する
''' </summary>
''' <param name="fileName">ファイル名(フルパスではなくファイル名のみを想定)</param>
''' <returns>安全であればTrue、そうでなければFalse</returns>
Function IsSafeExtension(ByVal fileName As String) As Boolean
    Dim dotPosition As Long
    Dim ext As String
    Dim allowedExtensions As Variant
    Dim i As Integer
    
    ' 1. 許可する拡張子のホワイトリスト(小文字で定義するのがコツ)
    ' ブラックリスト(禁止リスト)方式ではなく、ホワイトリスト方式が鉄則です。
    allowedExtensions = Array(".pdf", ".jpg", ".png", ".xlsx", ".docx")
    
    ' 2. 末尾からドットの位置を探す
    dotPosition = InStrRev(fileName, ".")
    
    ' ドットが見つからない、またはドットがファイル名の先頭にある場合は不正とみなす
    If dotPosition <= 0 Or dotPosition = Len(fileName) Then
        IsSafeExtension = False
        Exit Function
    End If
    
    ' 3. 拡張子部分を抽出して小文字に変換(大文字 .PDF にも対応するため)
    ext = LCase(Mid(fileName, dotPosition, Len(fileName) - dotPosition + 1))
    
    ' 4. ホワイトリストに含まれているかチェック
    IsSafeExtension = False
    For i = LBound(allowedExtensions) To UBound(allowedExtensions)
        If ext = allowedExtensions(i) Then
            IsSafeExtension = True
            Exit For
        End If
    Next i
End Function

4. エンジニアの視点:ここが「プロ」の設計思想

このコードには、初級者が飛ばしがちな「設計の要諦」が3つ含まれています。

① ホワイトリスト方式の採用

「`.exe` はダメ、`.bat` もダメ……」と禁止リスト(ブラックリスト)を作ると、新しい形式のウイルスが現れた瞬間に無防備になります。
「許可したもの以外はすべて拒否する」 というホワイトリスト方式こそが、セキュリティの基本原則です。

② `LCase` による正規化

ファイル名は `REPORT.PDF` と `report.pdf` が混在することがあります。判定前に `LCase` 関数で小文字に統一(正規化)することで、比較漏れを防ぎ、ロジックをシンプルに保っています。

③ `InStrRev` と `Mid` の組み合わせ

`Right` 関数で「4文字」と決め打ちすると、`.jpeg`(5文字)や `.docx`(5文字)に対応できません。
`InStrRev` でドットの位置を特定し、そこから末尾までを `Mid` で切り出す。この手法なら、拡張子の文字数に関わらず、常に正確に「真の拡張子」を掴み取ることができます。

まとめ:一歩先の自動化へ

いかがでしたでしょうか。
「動けばいい」という段階から、「安全に、かつ確実に動く」という段階へ。
今回学んだ `InStrRev` を使った拡張子判定は、Outlook VBAだけでなく、Excel VBAやVB.NETなど、あらゆるファイル操作において一生モノの技術になります。

「オブジェクトの振る舞いを予測し、最悪のケースを想定してコードを書く」

これができれば、あなたはもう立派なエンジニアです。
さあ、自信を持って、次の自動化プロジェクトに取り掛かってください。応援していますよ!

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