【実務・中級編】【スクリプト自己改ざん検知】自身のファイルハッシュと属性を起動時に検証するセーフティロジック – VBScript (Visual Basic Scripting Edition)解析バイブル

スポンサーリンク

【スクリプト自己改ざん検知】自身のファイルハッシュと属性を起動時に検証するセーフティロジック

業務自動化の現場において、VBScript(WSH)は今なお強力なカードだ。インストール不要で、Windows環境さえあればメモ帳とエディタ一つで即座に稼働する。この「手軽さ」ゆえに、多くのバックグラウンド処理や定型業務の自動化に組み込まれてきた。

だが、その手軽さは裏を返せば「誰でも容易に中身を書き換えられる」という致命的な脆弱性と隣り合わせであることを意味する。

共有サーバー上に配置されたVBScriptが、意図せぬ誤操作や、悪意ある第三者によってこっそり書き換えられたとしたらどうなるか? 業務ロジックがサイレントで破損し、気づかぬうちに間違ったデータがデータベースへ流し込まれる。最悪の場合、`WshShell.Run` を通じて任意のコマンドが実行される踏み台になりかねない。

プロフェッショナルな自動化エンジニアであれば、「動けばいい」という甘い考えを捨て、「コード自身が自身の完全性を担保する(セーフティ・インテグリティ)」仕組みを組み込まなければならない。

今回は、VBScriptの起動時に自分自身のファイルハッシュ(SHA-256)を計算し、あらかじめ安全性が確認された正当な値と突合、異常を検知した瞬間に安全な停止(Abort)を行う実戦的な自己診断ロジックを伝授する。

—

なぜ「簡易的なタイムスタンプチェック」では不十分なのか?

改ざん検知と聞いて、ファイルの「最終更新日時(DateLastModified)」をチェックするコードを書く素人がいる。

今すぐその設計は捨てなさい。

ファイルサーバー上の同期ズレ、ファイルのコピー&ペースト、あるいはタイムスタンプの偽装(Touchコマンド等)によって、更新日時はいかようにも変えられてしまう。信頼できるのはタイムスタンプではなく、ファイルの実データから算出される暗号学的ハッシュ値(SHA-256等)以外にない。

しかし、VBScript単体ではSHA-256を直接計算するネイティブなハッシュ関数を持っていない。COMコンポーネントやOSの標準ユーティリティをいかにスマートに、かつパフォーマンスを犠牲にせずに呼び出すかがアーキテクトの腕の見せ所だ。

—

設計アプローチ:CertUtilを活用したストリームレス・ハッシュ検証

Windows環境(Vista以降)には、証明書管理の標準ツールとして `certutil.exe` が常駐している。これを利用すれば、追加のDLLや外部ツールのインストールなしに、高精度なSHA-256ハッシュを取得できる。

処理の流れはこうだ:
1. 実行中の自分自身のパス(`WScript.ScriptFullName`)を動的に取得する。
2. `WshShell.Exec` を経由して `certutil -hashfile` を呼び出し、標準出力からハッシュ文字列をキャプチャする。
3. 取得したハッシュを、ハードコードまたは外部設定ファイル化された「正当なハッシュ値」と比較する。
4. 一致しなければ即座に `WScript.Quit` で異常終了し、ログまたはメッセージボックスで警告を発報する。

—

プロダクションコード:自己改ざん検知付きVBScriptテンプレート

以下のコードをコピーし、`.vbs` ファイルとして保存してほしい。実務の基盤スクリプトとしてそのまま組み込めるよう、エラーハンドリングとオブジェクトのライフサイクル管理を徹底している。

‘ ==============================================================================
‘ Script Name : IntegrityProtectedScript.vbs
‘ Description : 起動時に自身のSHA-256ハッシュを検証し、改ざんを検知するセーフティロジック
‘ Architect : Enterprise Automation Engineering Group
‘ ==============================================================================

Option Explicit

‘ メイン処理の実行
Call Main()

Sub Main()
Dim fso, wsh, selfPath, expectedHash, actualHash
Set fso = CreateObject(“Scripting.FileSystemObject”)
Set wsh = CreateObject(“WScript.Shell”)

‘ 1. 自分自身のファイルパスを取得
selfPath = WScript.ScriptFullName

‘ ————————————————————————–
‘ 【重要】初回リリース時、またはコードを正式に改修した後に算出された
‘ 正しいSHA-256ハッシュ値をここにハードコード(または安全な設定管理)する。
‘ ※最初の検証時は、この値を空欄にするかダミーにして、コンソールに出力された
‘  正しいハッシュ値をここに転記してください。
‘ ————————————————————————–
expectedHash = “A1B2C3D4E5F678901234567890ABCDEF1234567890ABCDEF1234567890ABCDEF”

‘ 2. 自身のファイルハッシュを算出
actualHash = GetFileSHA256(selfPath, wsh)

‘ ハッシュ取得に失敗した場合
If actualHash = “” Then
MsgBox “致命的なエラー: 自身のファイルハッシュを算出できませんでした。”, vbCritical, “セーフティ診断エラー”
WScript.Quit 99
End If

‘ 3. デバッグ用(初回構築時のみコメントアウトを解除して正しいハッシュを確認すること)
‘ WScript.Echo “Current Hash: ” & actualHash

‘ 4. ハッシュ値の突合(整合性チェック)
‘ ※初回構築時は expectedHash がダミーのため必ず不一致になります。実運用時は一致させます。
If UCase(Trim(actualHash)) <> UCase(Trim(expectedHash)) Then
‘ 改ざん・破損検知時の処理(イベントログへの記録、管理者への通知など)
MsgBox “【SECURITY ALERT】” & vbCrLf & _
“スクリプトの改ざんまたは破損を検知しました!” & vbCrLf & _
“実行を安全に中断します。” & vbCrLf & vbCrLf & _
“対象ファイル: ” & selfPath, vbCritical, “自己改ざん検知システム”

‘ 異常終了コードを返して強制終了
WScript.Quit 1
End If

‘ ==========================================================================
‘ ここから下が本来の業務ロジック(インテグリティが保証された安全な領域)
‘ ==========================================================================
Call RunBusinessLogic()

‘ オブジェクトの明示的な解放(メモリリーク防止の基本)
Set wsh = Nothing
Set fso = Nothing
End Sub

‘ ——————————————————————————
‘ Function : GetFileSHA256
‘ Description : CertUtilを使用して指定ファイルのSHA-256ハッシュ文字列を取得する
‘ ——————————————————————————
Function GetFileSHA256(filePath, wshObject)
Dim execObj, stdOut, line, hashVal
hashVal = “”

On Error Resume Next
‘ CertUtilコマンドを実行(ウィンドウを非表示にするために 0, False を指定)
Dim cmd
cmd = “certutil -hashfile “”” & filePath & “”” SHA256″
Set execObj = wshObject.Exec(cmd)

If Err.Number <> 0 Then
GetFileSHA256 = “”
Exit Function
End If
On Error GoTo 0

‘ 標準出力から結果を読み取る
‘ CertUtilの出力仕様:
‘ 1行目: 20ファイルの…
‘ 2行目: ハッシュ値(16進数文字列)
‘ 3行目: コマンドは正常に完了しました。
Do While Not execObj.StdOut.AtEndOfStream
line = Trim(execObj.StdOut.ReadLine)
‘ ハッシュ値の長さ(SHA-256は64文字の16進数)を簡易判定基準にする
If Len(line) = 64 And IsHex(line) Then
hashVal = line
Exit Do
End If
Loop

Set execObj = Nothing
GetFileSHA256 = hashVal
End Function

‘ ——————————————————————————
‘ Function : IsHex
‘ Description : 文字列が16進数で構成されているかを判定する補助関数
‘ ——————————————————————————
Function IsHex(str)
Dim i, c
IsHex = True
For i = 1 To Len(str)
c = Mid(str, i, 1)
If Not ((c >= “0” And c <= "9") or (UCase(c) >= “A” And UCase(c) <= "F")) Then IsHex = False Exit Function End If Next End Function ' ------------------------------------------------------------------------------ ' Sub : RunBusinessLogic ' Description : 本来の業務自動化処理(ここに実際の処理を記述する) ' ------------------------------------------------------------------------------ Sub RunBusinessLogic() WScript.Echo "インテグリティチェックを正常に通過しました。" & vbCrLf & _ "業務自動化プロセスを開始します..." ' TODO: ここに実業務の自動化コード(Excel操作、DB連携、ファイル転送など)を記述 End Sub ---

現場のアーキテクトが教える、運用上の極意と注意点

1. 初回デプロイ時のハッシュ値確定フロー
スクリプトを新しく作成・改修した直後は、`expectedHash` の値が一致しないため必ず弾かれる。一度コード内の `expectedHash` をダミーにして実行し、デバッグ用メッセージなどで出力された正確なSHA-256ハッシュ値をコードに貼り付け直して最終版とする、というビルドプロセス(運用ルール)をチーム内で徹底すること。

2. ネットワークドライブ上の実行パフォーマンス
共有サーバー(SMB環境)上に置かれたスクリプトに対して `certutil` を走らせる場合、ファイルサイズが大きいとわずかにオーバーヘッドが発生する。VBScriptファイル自体は数KB〜数十KB程度に収まるためパフォーマンス上の問題はほぼないが、不要な巨大テキストやアセットをVBScript内に直接埋め込む(Base64埋め込み等)アンチパターンは避けるべきだ。

3. セキュリティの限界を知る
このロジックは、「誤操作によるファイルの破損」や「無許可の軽微な改ざん」に対する強力な抑止力・検知手段となる。ただし、ソースコードにハッシュの比較ロジックが平文で書かれている以上、極めて高度な攻撃者がスクリプトファイル本体と `expectedHash` の両方を同時に書き換えた場合はすり抜けられる。
真にセキュアな環境が求められる場合は、ハッシュ値をコード内ではなく、アクセス権が厳格に制御された外部の読取専用データベースや、強固な権限管理がなされた環境変数・レジストリから動的に取得する設計へと昇華させるとよい。

結びにかえて

「動けばいい」で作られた脆弱なスクリプトは、やがて組織の信頼を揺るがす「技術的負債の爆弾」へと変貌する。
プロフェッショナルな自動化エンジニアであれば、コードの利便性だけでなく、その実行の正当性(インテグリティ)までをデザインに組み込むべきだ。

今回紹介した自己改ざん検知ロジックを取り入れ、組織の基幹を支えるにふさわしい、堅牢で信頼性の高い自動化ソリューションを構築してほしい。

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