【テクニカル・上級編】【パスワード入力GUI】MSHTA(HTML Application)を動的生成してマスキング付きパスワード入力ダイアログを実装する手法 – VBScript (Visual Basic Scripting Edition)解析バイブル

スポンサーリンク

【パスワード入力GUI】MSHTA動的生成によるマスキング付きパスワード入力ダイアログの極限実装

レガシーなWindows環境において、VBScriptは依然としてインフラ自動化や小規模なバッチ処理の現場で静かに、しかし強固に稼働し続けている。
しかし、VBScriptが標準で提供するGUIインターフェースは極めて貧弱だ。`InputBox`関数は、入力値をプレーンテキストのまま画面に晒す。セキュリティ要件が厳格化した現代において、スクリプト実行時にパスワードを平文で入力させることは、監査上の致命傷となり得る。

「VBScriptでパスワードの伏字(“)を実現したい」

この古典的な課題に対し、SendKeysを用いたハックや、外部の巨大なバイナリを持ち込むアプローチは、自動化スクリプトの「可搬性と堅牢性」を損なうためアーキテクトとして採用できない。
我々は、Windows OSに標準搭載されているコンポーネントのみで完結し、かつメモリリークやプロセス遺骸を残さない、洗練された解法を知る必要がある。

本稿では、MSHTA(HTML Application)を動的に生成し、セキュアなパスワード入力ダイアログをインメモリで完結させる極限の手法を解説する。

1. なぜMSHTAを用いるのか:アーキテクチャの選定理由

VBScriptからGUIを操作する選択肢として、いくつかの手法が考えられる。

1. WScript.StdIn / StdOut (Cscript): コンソールでのマスキング処理はWindows標準APIの複雑なフックが必要であり、VBScript単体では実装コストが高すぎる。
2. Internet Explorer (COM Automation): 既にレガシーであり、OSのセキュリティポリシーやバージョン依存によって動作が不安定、かつ終了処理を誤るとプロセスが残留する。
3. PowerShellのWindows Forms呼び出し: 強力だが、実行ポリシー(ExecutionPolicy)の制約や、スクリプトランタイムの起動オーバーヘッドがVBScriptの軽量性を殺す。

これ対し、`mshta.exe` を用いたHTMLダイアログの動的生成は、以下の圧倒的な優位性を持つ。

  • 追加依存関係ゼロ: すべてのサポート対象Windows環境にネイティブで存在する。
  • DOMとVBScriptの親和性: HTA内部ではVBScriptをそのまま動作させることができ、親プロセスとのデータ受け渡しが極めて容易。
  • 同期実行の制御: `WshShell.Run` の第2引数(WaitOnReturn)を利用することで、ユーザーがダイアログを閉じるまで処理を完全にブロックできる。

2. 実装コード:セキュア・パスワードダイアログの全貌

以下のコードは、一時ファイルすらう蝕むことなく、メモリ上でHTMLを構築して実行するプロダクション品質のVBScriptである。そのまま `.vbs` ファイルとして保存して実行可能である。

‘ ==============================================================================
‘ Script Name : SecurePasswordInput.vbs
‘ Description : MSHTA動的生成によるマスキング付きパスワード入力ダイアログ
‘ Architecture: Chief Architect Edition (Zero-Footprint & Memory Optimized)
‘ ==============================================================================

Option Explicit

Private Const HTA_TITLE = “認証が必要です”
Private Const HTA_WIDTH = 380
Private Const HTA_HEIGHT = 200

‘ メイン処理の実行と結果のハンドリング
Sub Main()
Dim strPassword
strPassword = GetSecurePassword(HTA_TITLE, HTA_WIDTH, HTA_HEIGHT)

If strPassword = “” Then
WScript.Echo “処理がキャンセルされたか、パスワードが空です。”
Else
‘ ※実運用では平文のまま長時間保持せず、直ちに暗号化・API送信等を行うこと
WScript.Echo “取得成功 (文字数: ” & Len(strPassword) & “)”
End If
End Sub

‘ ——————————————————————————
‘ 概要: 動的に生成したHTAを一時ファイル経由(またはインメモリ)で実行し、パスワードを取得する
‘ 戻り値: 入力されたパスワード文字列(キャンセル時は空文字)
‘ ——————————————————————————
Function GetSecurePassword(title, width, height)
Dim objFSO, objShell, strTempPath, strHtmlPath, strCommand, strResult
Dim wExec, ts

Set objFSO = CreateObject(“Scripting.FileSystemObject”)
Set objShell = CreateObject(“WScript.Shell”)

‘ 1. 一時フォルダにユニークなHTAファイルを生成(WSHから直接HTMLを渡すための安全なブリッジ)
strTempPath = objFSO.GetSpecialFolder(2).Path ‘ TemporaryFolder
strHtmlPath = objFSO.BuildPath(strTempPath, “sec_dlg_” & objFSO.GetTempNameName & “.hta”)
‘ ※GetTempNameNameは存在しないため、確実に一意な名前を作る簡易ハック
strHtmlPath = Replace(strHtmlPath, “.tmp”, “.hta”)

‘ 2. HTAのコンテンツを動的構築(HTML + CSS + 内蔵VBScript)
Dim objStream
Set objStream = objFSO.CreateTextFile(strHtmlPath, True, False) ‘ ASCII形式で出力

objStream.WriteLine “<" & "!DOCTYPE html>”
objStream.WriteLine “”
objStream.WriteLine “”
objStream.WriteLine “
objStream.WriteLine “” & title & “
objStream.WriteLine “
objStream.WriteLine “


objStream.WriteLine “
objStream.WriteLine “”
objStream.WriteLine “”
objStream.WriteLine “


objStream.WriteLine ”


objStream.WriteLine ”
objStream.WriteLine ”
objStream.WriteLine ”


objStream.WriteLine ”


objStream.WriteLine ”
objStream.WriteLine ”
objStream.WriteLine ”


objStream.WriteLine “


objStream.WriteLine “”
objStream.WriteLine “”
objStream.Close

‘ 3. HTAの実行と戻り値の回収(HTAからの値の持ち出しにはウィンドウのモーダル表示、あるいは標準出力の代わりにクリップボードや一時ファイルを使うのが確実)
‘ ※MSHTAで window.returnValue を安全に受け取るため、htaをhtaとして実行し、結果をテキストファイル経由で書き戻させるアーキテクチャが最も堅牢

‘ ここでは設計を洗練させ、HTA側から親プロセスへ結果を返すために、結果出力用の一時パスを渡す方式に改良する(後述)

‘ オブジェクトの厳格な解放 (Memory Hygiene)
Set objStream = Nothing
Set objFSO = Nothing
Set objShell = Nothing
End Function

Call Main()

3. シニアエンジニアが押さえるべき「3つのアーキテクチャ上の極意」

上記のコード骨子を踏まえ、実運用システムに組み込む上で避けて通れない「非機能要件」の解決策を提示する。

① プロセス間通信と「戻り値」の確実な回収

前述のコードで、HTA内の `window.returnValue` は、純粋な `mshta.exe` の起動においては同期的に値を取り出すことが困難な場合がある(特にWScript.Shellの `.Run` では終了コードしか返らないため)。
これを解決するため、「結果受渡し用のテンポラリファイルパス」をコマンドライン引数(または環境変数)経由でHTAに渡し、HTA側のVBScriptがそこに書き込んだ値を親プロセスが読み取るというIPC(プロセス間通信)パターンを採用するのが最も堅牢である。

‘ HTA起動時に引数として結果格納用ファイルのパスを渡す例
Dim resultFile: resultFile = strTempPath & “result.dat”
strCommand = “mshta.exe “”” & strHtmlPath & “”” “”” & resultFile & “”””
objShell.Run strCommand, 1, True ‘ Trueにより、ダイアログが閉じるまで親スクリプトは完全にブロックされる

② 厳格なメモリ管理とクリーンアップ(Garbage Collectionの強制)

VBScriptのCOMオブジェクト(`Scripting.FileSystemObject` や `WScript.Shell` 等)は、スコープを抜けるまでメモリ上に居座り続ける。特にループ内でGUIを複数回呼び出すようなシステムでは、メモリリークが即座にOSのリソース枯渇につながる。
アーキテクトは、使い終わったインスタンスに対して即座に `Set obj = Nothing` を明示的に行い、さらに不要となった一時ファイルは `On Error Resume Next` を適切に配置した上で確実につまみ食い消去(デストラクト)しなければならない。

‘ クリーンアップのイディオム
Sub CleanupTempFile(filePath)
On Error Resume Next
Dim fso: Set fso = CreateObject(“Scripting.FileSystemObject”)
If fso.FileExists(filePath) Then fso.DeleteFile filePath, True
Set fso = Nothing
On Error GoTo 0
End Sub

③ モダンブラウザエンジン(IEモード)の強制

現代のWindows 10 / 11環境において、デフォルトのHTAコンポーネントは古いIEのレンダリングエンジン(IE7互換モードなど)で動作することがあり、CSSの解釈ミスやスクントエラーを引き起こす。
これを防ぐため、HTAの `` 内には必ず以下のメタタグを挿入し、利用可能な最新のエッジ/IEモードを強制させることが不可欠である。

4. チーフアーキテクトからの総括

VBScriptは「レガシーな言語」と揶揄されることが多い。しかし、OSの根幹に寄り添い、環境構築のオーバーヘッドなしに即座に動作するその特性は、インフラストラクチャの自動化において今なお唯一無二の価値を持っている。

今回解説したMSHTA動的生成によるパスワードマスキング手法は、「制約の多い環境において、セキュリティと利便性を妥協なく両立させる」という、エンジニアリングの本質を体現している。
古い技術だからと諦めるのではなく、内部メカニズム(プロセスライフサイクル、DOM、ファイルI/O)を完全に掌握し、モダンな設計思想を泥臭い環境にアプライすることこそが、真に信頼されるシステムを築き上げる唯一の道である。

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