【入門編】【動的コード実行の罠】ExecuteGlobal を使用した共通設定読み込み時の変数スコープ汚染とセキュリティ対策 – VBScript (Visual Basic Scripting Edition)解析バイブル

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

【動的コード実行の罠】ExecuteGlobal を使用した共通設定読み込み時の変数スコープ汚染とセキュリティ対策

こんにちは。自動化の深淵へようこそ。

これまでVBScriptの基礎、変数やループ、条件分岐を学んできたあなたなら、「もっとコードを整理したい」「設定値だけを別ファイルに切り出して、スマートに管理したい」という欲求が湧いてきているはずです。

その時、ふと目にするのが `ExecuteGlobal` という命令です。「外部ファイルのテキストを、まるでその場で書いたコードのように実行できる」……。一見すると、魔法のような便利な道具に見えるでしょう。

しかし、プロフェッショナルへの階段を登るなら、この「魔法」の裏側に潜む恐ろしい代償を知っておかなければなりません。今日は、多くのエンジニアが一度は踏み抜く「スコープ汚染」と「セキュリティの落とし穴」について、じっくりとお話ししましょう。

ここをクリアすれば、あなたのVBScriptは「動くスクリプト」から「信頼されるシステム」へと進化しますよ。

1. `ExecuteGlobal` とは何者か?

まず、整理しておきましょう。VBScriptには、文字列をプログラムとして実行する命令が2つあります。

1. `Execute`: 文字列を実行しますが、その結果は「その場限り(ローカルスコープ)」に閉じ込められます。
2. `ExecuteGlobal`: 文字列を実行し、その中で定義された変数を「プログラム全体の共通変数(グローバルスコープ)」として扱います。

なぜ `ExecuteGlobal` が使われるのか? それは、「別ファイルに書いた設定値を、メインのスクリプトでそのまま変数として使いたいから」です。

魔法の使い方の例(一見、便利そうに見える)

例えば、`config.txt` というファイルにこう書いてあったとします。

' config.txt の中身
AppTitle = "業務自動化ツール v1.0"
MaxRetry = 5

これをメインのスクリプトで読み込むとき、`ExecuteGlobal` を使うとこう書けます。

' main.vbs
Dim fso, file, content
Set fso = CreateObject("Scripting.FileSystemObject")

' 設定ファイルを読み込む
If fso.FileExists("config.txt") Then
    Set file = fso.OpenTextFile("config.txt", 1)
    content = file.ReadAll
    file.Close
    
    ' ここが魔法の正体!
    ExecuteGlobal content 
End If

' 読み込んだ設定をそのまま使える!
WScript.Echo "起動中: " & AppTitle
WScript.Echo "最大リトライ回数: " & MaxRetry

「ほら、便利でしょう?」と思ったあなた。鋭いですね。ですが、ここからが本番です。

2. 陥りやすい罠その1:変数スコープの汚染

`ExecuteGlobal` は、読み込んだコードを「メインスクリプトと同じ階層(グローバル空間)」に叩き込みます。これが何を意味するか分かりますか?

「メインスクリプトで使っている大事な変数を、外部ファイルが勝手に上書きしてしまう可能性がある」ということです。

恐怖のシナリオ:意図しない上書き

以下のコードを見てください。

' main.vbs
Dim targetPath, retryCount
targetPath = "C:\Data\Output"
retryCount = 10 ' ← あなたが慎重に決めた大事な値

' --- 外部設定の読み込み ---
' もし config.txt の中に、うっかり「retryCount = 0」と書いてあったら……
ExecuteGlobal ReadFile("config.txt") 
' -----------------------

' ここで、targetPath や retryCount がどうなっているか?
WScript.Echo "出力先: " & targetPath
WScript.Echo "リトライ回数: " & retryCount ' ← ここが 0 に書き換わっている!

このように、外部ファイルの内容によってメインプログラムの動作が予測不能になる現象を「スコープ汚染」と呼びます。大規模な開発になればなるほど、どこで変数が書き換わったのかを追跡するのは至難の業になります。

3. 陥りやすい罠その2:セキュリティ・インジェクション

もう一つ、最も致命的な問題があります。それは「セキュリティ」です。

`ExecuteGlobal` は、読み込んだテキストを「命令」として実行します。もし、その設定ファイルが何らかの理由(共有フォルダでの誤操作や、悪意ある第三者による改ざん)で書き換えられたらどうなるでしょうか?

攻撃の例

`config.txt` の中に、設定値ではなく、こんなコードが紛れ込んでいたら……

' 悪意ある config.txt の中身
AppTitle = "Normal App"
' 勝手にコマンドプロンプトを開いて、ファイルを削除する命令を仕込む
CreateObject("WScript.Shell").Run "cmd /c del C:\ImportantData\*.*", 0, True

`ExecuteGlobal` は、これを「正しい命令」として疑わずに実行してしまいます。これが「コード注入(インジェクション)攻撃」です。プログラムの利便性と引き換えに、システム全体の制御権を明け渡してしまうリスクがあるのです。

4. プロフェッショナルな解決策:オブジェクトによる「カプセル化」

では、どうすれば安全に、かつスマートに設定を読み込めるのでしょうか?
答えはシンプルです。「変数を直接バラまかないこと」です。

変数をグローバルに展開するのではなく、`Dictionary`(連想配列)オブジェクト、あるいは`Class`(クラス)の中に閉じ込める(カプセル化する)のが、エンジニアとしての正解です。

【推奨】Dictionaryオブジェクトを使った安全な設定管理

この方法なら、変数が勝手に上書きされることも、外部から直接命令を実行されるリスクも劇的に減らせます。

' main.vbs (プロフェッショナル版)
Option Explicit ' 変数の宣言を強制する(これ、絶対やってくださいね!)

Dim fso, configDict
Set fso = CreateObject("Scripting.FileSystemObject")
Set configDict = CreateObject("Scripting.Dictionary")

' --- 設定の読み込み処理 ---
LoadConfig "config.txt", configDict

' --- メイン処理 ---
' 設定値へのアクセスは、必ず Dictionary を経由する
WScript.Echo "起動中: " & configDict("AppTitle")
WScript.Echo "最大リトライ回数: " & configDict("MaxRetry")

' 変数が汚染されていないか確認
' configDict("retryCount") とか、変な名前でアクセスしてもエラーになるか制御できる
' -----------------------

<-----------------------------------------------------------
Sub LoadConfig(filePath, dictObj)
    Dim fso, file, line, parts, key, value
    Set fso = CreateObject("Scripting.FileSystemObject")
    
    If Not fso.FileExists(filePath) Then
        WScript.Echo "Error: 設定ファイルが見つかりません。"
        WScript.Quit
    End If
    
    Set file = fso.OpenTextFile(filePath, 1)
    
    Do Until file.AtEndOfStream
        line = Trim(file.ReadLine)
        
        ' 空行やコメント(; で始まる行)を無視する
        If line <> "" And Left(line, 1) <> ";" Then
            ' 「キー=値」の形式を想定して分割
            If InStr(line, "=") > 0 Then
                parts = Split(line, "=")
                key = Trim(parts(0))
                value = Trim(parts(1))
                
                ' Dictionaryに格納(ここで「命令」として実行されることはない!)
                If Not dictObj.Exists(key) Then
                    dictObj.Add key, value
                End If
            End If
        End If
    Loop
    file.Close
End Sub
<-----------------------------------------------------------

このコードの何が素晴らしいのか?

1. `Option Explicit` の使用: 変数の宣言を強制することで、タイポ(打ち間違い)によるバグを未然に防いでいます。
2. コードではなく「データ」として扱う: `ExecuteGlobal` を使わず、テキストを一行ずつ読み、`=` で分割して `Dictionary` に入れています。たとえファイルの中に `Run “calc.exe”` と書いてあっても、それは単なる「文字列」として処理されるだけで、実行されることはありません。
3. カプセル化: 設定値はすべて `configDict` という一つの箱の中に守られています。メインスクリプトの変数と混ざることはありません。

まとめ:エンジニアとしての視点

`ExecuteGlobal` は、確かに手軽で、小規模な使い捨てスクリプトなら魔法のように機能します。しかし、あなたが「誰かに使われるツール」や「長く運用するシステム」を作ろうとしているなら、その魔法は手放すべきです。

  • `ExecuteGlobal` は「コード」を流し込む。(危険・汚染のリスクあり)
  • `Dictionary` は「データ」を流し込む。(安全・管理しやすい)

「動けばいい」という段階から、「どうすれば壊れないか、どうすれば悪用されないか」を考える段階へ。この視点の切り替えこそが、初学者とプロフェッショナルを分ける境界線です。

ここをクリアすれば、あなたのVBScriptの基礎はバッチリですよ。
さあ、自信を持って、次の自動化に挑戦してください!

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