【上級プロフェッショナル向け】Windowsイベントログ連携によるFSO重要データ削除・改変のリアルタイムSIEM通知スクリプト
プロフェッショナルな現場において、ファイルサーバーやローカルストレージ上の「重要データの改変・削除検知」は、セキュリティ監査およびインシデント初動対応の要諦である。
多くのエンジニアは、ファイルシステムオブジェクト(FSO: FileSystemObject)を用いて単にファイルの有無を確認したり、ログファイルをテキスト形式でローカルに追記したりするだけの稚拙な実装で満足している。しかし、それでは「ログファイル自体が改ざんされた場合」に真相が闇に葬られるうえ、企業全体のSOC(Security Operation Center)やSIEM(Splunk, Microsoft Sentinel, Datadogなど)とのリアルタイム連携において完全に機能不全に陥る。
今回は、FSOの監視・操作イベントをトリガーとし、Windowsのネイティブイベントログ(セキュリティ/アプリケーションログ)へ直接フックさせ、エンタープライズ監視網へシームレスに流し込むための「極限まで堅牢化されたプロダクションコード」を伝授する。
—
なぜ「テキストファイルへの直接ロギング」は実務で破綻するのか
素人が書く自動化スクリプトの悪癖として、次のような実装が挙げられる。
‘ 【アンチパターン】絶対に真似してはならない実装
Dim fso, logFile
Set fso = CreateObject(“Scripting.FileSystemObject”)
Set logFile = fso.OpenTextFile(“C:\Logs\operation.log”, 8, True)
logFile.WriteLine Now & ” – ファイルが削除されました: ” & targetPath
logFile.Close
このアプローチが実務で破綻する理由は明確だ。
1. 競合制御の欠如: 複数プロセスが同時に書き込みを行った際、I/Oエラー(エラー70: 書き込み権限がありません、またはロックされています)でスクリプトが即座にクラッシュする。
2. 監査証拠としての信頼性ゼロ: ローカルのテキストファイルは、管理者権限を持つ悪意あるユーザーによって容易に改ざん・削除が可能であり、フォレンジックの証拠能力を持たない。
3. SIEM統合の欠如: 監視エージェントがテキストのパース(行解析)を行う必要があるため、フォーマット変更に弱く、リアルタイム性も著しく劣る。
これを解決唯一の解こそが、`WshShell.LogEvent`を用いたWindowsイベントログへのダイレクト・インジェクションである。OSのイベントログ機構に流し込むことで、ACL(アクセス制御リスト)による保護、イベントビューアからの視認性、そして各種SIEMフォワードツールによる秒単位のクラウド転送が完全に担保される。
—
堅牢なFSO × イベントログ連携アーキテクチャ
今回のプロダクションコードでは、以下の設計思想を徹底している。
- 厳格なエラーハンドリング (`On Error Resume Next` の局所化): FSOのI/Oエラーや、存在しないパスへのアクセス時にもスクリプトが死なない構造。
- イベントカテゴリの分離: 正常操作と危険な削除・改変をイベントIDレベルで明確に分離。
- WMI/WshShellの適切なライフサイクル管理: メモリリークを防ぐためのオブジェクト解放の徹底。
—
【プロダクションコード】実用FSO監査・イベント通知スクリプト
以下のコードは、指定した重要ファイル/フォルダに対して削除・改変の兆候を検知し、Windowsイベントログへ構造化されたメッセージを書き込む、そのままエンタープライズ環境に投入可能なスクリプトである。
‘ ==============================================================================
‘ Script Name : FSO_SecurityAudit_Notifier.vbs
‘ Description : FSOを用いたファイル操作監視とWindowsイベントログ連携によるSIEM通知
‘ Author : 首席アーキテクト
‘ ==============================================================================
Option Explicit
‘ 定数の定義
Const EVENT_SUCCESS = 0
Const EVENT_ERROR = 1
Const EVENT_WARNING = 2
Const EVENT_INFO = 4
Const TARGET_FILE = “C:\EnterpriseData\Confidential_Master.dat”
Const EVENT_SOURCE = “EnterpriseSecurityAuditor”
‘ メイン処理の実行
Call MonitorAndAuditFile(TARGET_FILE)
Sub MonitorAndAuditFile(filePath)
Dim fso, wshShell, fileObj
Dim fsoExists, fsoSize, fsoLastModified
‘ オブジェクトの生成
On Error Resume Next
Set fso = CreateObject(“Scripting.FileSystemObject”)
Set wshShell = CreateObject(“WScript.Shell”)
If Err.Number <> 0 Then
Call WriteSystemLog(wshShell, EVENT_ERROR, 1001, “FSOまたはWshShellオブジェクトの生成に失敗しました。Error: ” & Err.Description)
Exit Sub
End If
On Error GoTo 0
‘ 【重要】イベントソースの登録確認(初回実行時等のためのフォールバック)
‘ ※ 本番環境ではレジストリにイベントソースが事前登録されている前提とする
‘ ファイルの存在確認とメタデータ取得
If fso.FileExists(filePath) Then
Set fileObj = fso.GetFile(filePath)
fsoSize = fileObj.Size
fsoLastModified = fileObj.DateLastModified
‘ 監視ロジック(例:サイズが異常に小さい、または特定条件を満たした場合の改変検知)
If fsoSize < 100 Then
Dim alertMsg
alertMsg = "【セキュリティ警告】重要データのサイズが閾値を下回りました。改変の可能性があります。" & vbCrLf & _
"パス: " & filePath & vbCrLf & _
"サイズ: " & fsoSize & " bytes" & vbCrLf & _
"最終更新日時: " & fsoLastModified
' イベントログへ警告を出力 (Event ID: 3001 = データ改変の兆候)
Call WriteSystemLog(wshShell, EVENT_WARNING, 3001, alertMsg)
Else
' 正常稼働ログの記録(必要に応じてデバッグレベルで出力)
' Call WriteSystemLog(wshShell, EVENT_INFO, 2001, "ファイル監査: 正常性確認完了 -> ” & filePath)
End If
Set fileObj = Nothing
Else
‘ 【最重要】重要ファイルの消失(削除)検知
Dim criticalMsg
criticalMsg = “【緊急事態】保護対象の重要ファイルが消失(削除)されました!” & vbCrLf & _
“消失パス: ” & filePath & vbCrLf & _
“実行ユーザー: ” & wshShell.ExpandEnvironmentStrings(“%USERNAME%”) & vbCrLf & _
“端末名: ” & wshShell.ExpandEnvironmentStrings(“%COMPUTERNAME%”)
‘ イベントログへ重大エラーを出力 (Event ID: 4001 = 重要データ削除)
Call WriteSystemLog(wshShell, EVENT_ERROR, 4001, criticalMsg)
End If
‘ オブジェクトの明示的解放(メモリリークの根絶)
Set fso = Nothing
Set wshShell = Nothing
End Sub
Sub WriteSystemLog(shObj, logType, eventId, message)
On Error Resume Next
‘ WshShell.LogEvent の構文: LogEvent(Type, Message, [Target])
‘ Type: 0(Success), 1(Error), 2(Warning), 4(Information)
Dim formattedMessage
formattedMessage = “[EventID:” & eventId & “] ” & message
shObj.LogEvent logType, formattedMessage, EVENT_SOURCE
If Err.Number <> 0 Then
‘ イベントログ書き込み自体が失敗した場合は、最悪のケースとしてWScript.Echoでフォールバック
‘ (本番ではOSのアプリケーションログへのフォールバック等を検討すること)
WScript.Echo “CRITICAL: イベントログへの書き込みに失敗しました – ” & Err.Description
Err.Clear
End If
On Error GoTo 0
End Sub
—
プロフェッショナルが解説する実装の急所
1. イベントソース(`EVENT_SOURCE`)の設計
`WshShell.LogEvent` を使用する場合、デフォルトではソース名が `WSH` としてアプリケーションログに記録される。これでは、どのスクリプト、どのセキュリティ監査ツールからのログであるかが判別がつかない。
本コードのように独自のソース文字列を指定することで、SIEM側のパーサー(Regex等)で容易にフィルタリングが可能になる。
(※注: 初回実行時にイベントソースのレジストリ登録がない環境では、イベントビューアの詳細説明に「説明文が見つかりません」と表示される場合があるが、ログとしての実データ・EventIDは確実に記録されるためSIEM連携には何ら支障はない)
2. オブジェクトのライフサイクル管理とメモリ最適化
VBScriptのガベージコレクションは信頼性が低い。特にタスクスケジューラから毎分実行されるような監視スクリプトにおいて、`Set fso = Nothing` を怠ると、COMコンポーネントの参照カウントが残存し、メモリリーク(Memory Leak)を引き起こす。
プロダクションコードでは、サブルーチンの終了直前に必ず `Nothing` を代入し、リソースを完全にOSへ返還している。
3. セキュリティ監査における「誰が」の特定
ファイル削除(Event ID: 4001)の検知時、単に「消された」事実だけでなく、環境変数から取得した `%USERNAME%` と `%COMPUTERNAME%` をメッセージに埋め込んでいる点に注目してほしい。
これにより、イベントログを収集したSIEM上で、「どの端末の、どのユーザーアカウントによって重要データが葬り去られたのか」を即座に特定し、EDR(Endpoint Detection and Response)と連動したアカウントの自動隔離フローへつなげることが可能となる。
—
運用時の注意点とインフラ連携の極意
- タスクスケジューラとの統合: このスクリプトは単体で常時常駐するものではない(VBScriptで無限ループを回すのはリソースの無駄であり悪手である)。Windowsタスクスケジューラに登録し、「最特権で実行(Run with highest privileges)」にチェックを入れ、高頻度(例:5分おき)でバッチ実行させるのが最も堅牢である。
- SIEMフォワーダーの設定: Splunk Universal ForwarderやMicrosoft Monitoring Agentに対し、アプリケーションログを監視させ、ソース名 `EnterpriseSecurityAuditor` かつ イベントID `4001` が検知された瞬間に、セキュリティ担当者のSlack/Teams、あるいはPushoverへWebhook飛散するパイプラインを構築せよ。
VBScriptはレガシーな言語と揶揄されることもあるが、OSの根幹API(WshShellやFSO)をダイレクトかつローコストに叩ける特性において、いまだにWindowsインフラ管理の最強の隠し剣である。
本稿の知見を実装に落とし込み、貴社のセキュリティ監視体制をワンランク上のステージへと引き上げてほしい。
