【上級プロフェッショナル向け】CertUtil連携によるファイルハッシュ(SHA256)比較とFSOを組み合わせた完全一致データバックアップ
レガシーシステムの深淵において、VBScriptは今なおインフラの自動化やバッチ処理の基盤として静かに、しかし強固に稼働し続けている。
「枯れた言語」と揶揄されることもあるが、OS標準のコンポーネントのみで完結し、追加のランタイムを要求しないその特性は、厳格なセキュリティポリシーが敷かれたエンタープライズ環境において圧倒的な優位性を持つ。
ファイルシステムオブジェクト(FSO: `Scripting.FileSystemObject`)を用いたファイル・フォルダ操作はVBScriptの基本中の基本であるが、単なる `CopyFile` やタイムスタンプの比較だけで「同期完了」と判断しているようであれば、それはプロフェッショナルの仕事とは言えない。
ファイルの日付やサイズが一致していても、ネットワーク転送時のビット化けやストレージの微細な破損(ビットロット)を検知することは不可能だからだ。
本稿では、Windows標準コマンドである `CertUtil` の暗号学的ハッシュ(SHA256)抽出機能と、FSOによるメタデータ検証を高度に融合させ、ビット単位の完全性を担保した超高信頼性データバックアップシステムの構築手法を、極限の知見とともに解説する。
—
1. アーキテクチャの設計思想:なぜ「FSO単体」では不十分なのか
FSOが提供する `GetFile` メソッドは、ファイルのサイズ、作成日時、最終更新日時、属性などを高速に取得できる。しかし、これらはあくまで「メタデータ」に過ぎない。
真に堅牢なバックアップシステムを構築するためには、以下の3段階のバリデーションを通過させる必要がある。
1. 高速プレチェック(FSO): ファイルサイズの一致を確認し、明らかに異なるファイルの無駄なハッシュ計算を排除する。
2. 暗号学的同一性検証(CertUtil): SHA256ハッシュを算出し、データの中身そのものが1ビットも違わないことを証明する。
3. 安全なI/Oストリーム制御(WScript.Shell): 標準出力のパイプラインを正確に捕捉し、メモリリークやプロセスのゾンビ化を防ぐ。
—
2. 実装コード:完全一致データ同期エンジン
以下のスクリプトは、単なるコピー&ペーストの範疇を超え、オブジェクトのライフサイクル管理、エラーハンドリング、標準出力のストリーム解析を極めたプロダクション品質のコードである。
‘ ==============================================================================
‘ Script Name: SecureSyncEngine.vbs
‘ Description: FSOとCertUtil(SHA256)を統合したビット単位完全一致バックアップ
‘ Author: Chief Architect (Legacy Systems Integration)
‘ ==============================================================================
Option Explicit
‘ 定数の定義
Const OverwriteExisting = True
Const ReadOnly = 1
‘ メイン処理の実行
Sub Main()
Dim sourcePath, destPath
sourcePath = “C:\Data\Source\critical_data.dat”
destPath = “D:\Backup\critical_data.dat”
WScript.Echo “[INFO] 同期プロセスを開始します: ” & sourcePath
If SyncFileWithHashValidation(sourcePath, destPath) Then
WScript.Echo “[SUCCESS] データの整合性検証完了。同期が正常終了しました。”
Else
WScript.Echo “[ERROR] 同期または整合性検証に失敗しました。”
End If
End Sub
‘ ——————————————————————————
‘ 関数名: SyncFileWithHashValidation
‘ 概要: ファイルサイズおよびSHA256ハッシュを比較し、必要に応じて安全にコピーと検証を行う
‘ ——————————————————————————
Function SyncFileWithHashValidation(ByVal src, ByVal dst)
Dim fso, srcFile, dstFile
Set fso = CreateObject(“Scripting.FileSystemObject”)
‘ ソースファイルの存在確認
If Not fso.FileExists(src) Then
WScript.Echo “[ERROR] ソースファイルが存在しません: ” & src
SyncFileWithHashValidation = False
Exit Function
End If
Set srcFile = fso.GetFile(src)
‘ デスティネーション側のファイルが存在するか確認
If fso.FileExists(dst) Then
Set dstFile = fso.GetFile(dst)
‘ 高速プレチェック: サイズが完全一致するか
If srcFile.Size = dstFile.Size Then
WScript.Echo “[DEBUG] ファイルサイズが一致しました。SHA256ハッシュを検証します…”
Dim srcHash, dstHash
srcHash = GetSHA256Hash(src)
dstHash = GetSHA256Hash(dst)
If srcHash = dstHash And srcHash <> “” Then
WScript.Echo “[INFO] ハッシュ値が完全に一致しています。コピーをスキップします。”
‘ オブジェクトの明示的解放
Set dstFile = Nothing
Set srcFile = Nothing
Set fso = Nothing
SyncFileWithHashValidation = True
Exit Function
End If
End If
Set dstFile = Nothing
End If
‘ コピー実行(FSO)
WScript.Echo “[INFO] ファイルをコピーしています…”
fso.CopyFile src, dst, OverwriteExisting
‘ コピー後の事後検証
Dim finalSrcHash, finalDstHash
finalSrcHash = GetSHA256Hash(src)
finalDstHash = GetSHA256Hash(dst)
If finalSrcHash = finalDstHash And finalSrcHash <> “” Then
WScript.Echo “[INFO] コピー後のハッシュ検証に成功しました: ” & finalSrcHash
SyncFileWithHashValidation = True
Else
WScript.Echo “[FATAL] コピー後のハッシュ一致検証に失敗しました!データ破損の恐れがあります。”
SyncFileWithHashValidation = False
End If
‘ オブジェクトのライフサイクル管理:メモリリーク防止のための明示的解放
Set srcFile = Nothing
Set fso = Nothing
End Function
‘ ——————————————————————————
‘ 関数名: GetSHA256Hash
‘ 概要: CertUtilコマンドを呼び出し、対象ファイルのSHA256ハッシュ文字列を抽出する
‘ ——————————————————————————
Function GetSHA256Hash(ByVal filePath)
Dim shell, exec, output, line, hashValue
Set shell = CreateObject(“WScript.Shell”)
‘ CertUtilの実行(-hashfile
‘ ※環境言語やOSバージョンにより出力フォーマットが異なるため、パース処理に注意
Dim command
command = “certutil -hashfile “”” & filePath & “”” SHA256″
On Error Resume Next
Set exec = shell.Exec(command)
If Err.Number <> 0 Then
WScript.Echo “[ERROR] CertUtilの実行に失敗しました: ” & Err.Description
GetSHA256Hash = “”
Exit Function
End If
On Error GoTo 0
hashValue = “”
‘ 標準出力を走査
Do While Not exec.StdOut.AtEndOfStream
line = Trim(exec.StdOut.ReadLine)
‘ CertUtilの出力仕様:ハッシュ値は通常2行目にダッシュなしの16進数文字列として出力される
‘ 例:
‘ SHA256 の
‘ a1b2c3d4e5f6…
‘ CertUtil: -hashfile コマンドは正常に完了しました。
If Not (Left(line, 5) = “CertU” Or Left(line, 4) = “SHA2” Or line = “”) Then
‘ スペースを除去してハッシュ値のみを抽出
hashValue = Replace(line, ” “, “”)
Exit Do
End If
Loop
GetSHA256Hash = hashValue
‘ オブジェクトの明示的解放
Set exec = Nothing
Set shell = Nothing
End Function
‘ エントリポイントの呼び出し
Main()
—
3. チーフアーキテクトが解説する「極限の知見」と実装の急所
A. オブジェクトのライフサイクルとメモリガベージコレクション
VBScriptのCOMコンポーネント(`Scripting.FileSystemObject` や `WScript.Shell`)は、スコープを抜けるまでメモリ上に居座り続ける。特にループ内でこれらのオブジェクトをインスタンス化し、`Set … = Nothing` による明示的な破棄を怠ると、VBScriptホスト(wscript.exe / cscript.exe)のメモリ使用量が肥大化し、最終的にリソース枯渇を引き起こす。
プロダクションコードでは、スコープの大小に関わらず、使い終えたオブジェクトは即座に `Nothing` を代入してメモリを開放することが鉄則である。
B. `WScript.Shell.Exec` と `Run` の使い分け
外部コマンド(`CertUtil`)を叩く際、同期的に標準出力を取得するためには `Exec` メソッドが不可欠である。`Run` メソッドでは戻り値のコード(終了ステータス)しか取得できず、生成されたハッシュ値をスクリプト側に引き込むことができない。
`Exec` が返す `WScript.Process` オブジェクトの `StdOut` ストリームを `AtEndOfStream` でループ処理することにより、標準出力を確実にキャプチャし、パースすることが可能となる。
C. OSロケールと `CertUtil` の出力揺れ対策
`CertUtil` はWindows標準ツールであるが、OSの言語設定(日本語版か英語版か)やWindowsのビルドバージョンによって、出力されるメッセージヘッダーの文字列が異なる。
そのため、単純に「上から2行目」といった行数依存のパースを行うと、環境差異によってスクリプトが破綻する。
上記のコードでは、行の先頭文字列(`CertU` や `SHA2`)や空行を動的にスキップし、純粋なハッシュ文字列(16進数かつ適切な長さを持つ文字列)のパターンを捉えるロジックを組むことで、環境非依存の堅牢性を担保している。
—
4. 結び:レガシーの皮をかぶった最先端の堅牢性
「VBScriptは古い」という声を聞くたびに、私はこう言い返す。
「道具が古いのではない。それを扱うエンジニアの技量と執念が足りないだけだ」と。
OSの奥深く底流にあるネイティブコマンド(CertUtil)と、枯れて洗練されたFSOを組み合わせることで、サードパーティ製の巨大なバックアップソフトウェアルーチンに匹敵、あるいはそれ以上の透明性と信頼性を持つファイル同期システムを、わずか百行足らずのテキストで実現できる。
現場のインフラを預かるシニアエンジニア諸賢よ。真に信頼できる自動化とは、ブラックボックス化された高価なツールに依存することではなく、基盤の隅々まで意図を浸透させられる「コードの支配力」に宿るのである。
