【テクニカル・上級編】【安全な資格情報一時保持】WScript.Shell の Volatile 環境変数を利用した同一セッション内での一時トークン受け渡し – VBScript (Visual Basic Scripting Edition)解析バイブル

スポンサーリンク

【安全な資格情報一時保持】WScript.Shell の Volatile 環境変数を利用した同一セッション内での一時トークン受け渡し

Windowsのシステム管理やレガシー移行の現場において、プロセス間で一時的なトークンやパスワード、認証状態を共有しなければならない局面は頻繁に発生します。

最も安易な解決策は「一時ファイルへの書き出し」ですが、これはディスクI/Oを発生させるだけでなく、スクリプトの異常終了時にファイルが残留し、資格情報が平文でディスク上に永続化するという致命的なセキュリティリスク(CWE-312: Cleartext Storage of Sensitive Information)を内包します。

この課題に対し、Windowsアーキテクチャが提供する「Volatile(揮発性)環境変数」は、エレガントかつ極めて実用的な解法となります。本稿では、`WScript.Shell` を用いたVolatile環境変数の制御手法、その内部構造、COMオブジェクトの厳密なライフサイクル管理、そしてセキュリティ境界としての限界と正しい暗号化併用のアプローチについて、アーキテクトの視点から徹底的に解説します。

—

1. Volatile(揮発性)環境変数の正体とライフサイクル

Windowsにおける環境変数には、その永続性とスコープに応じて複数の「ターゲット」が存在します。

| ターゲット | 格納場所 | ライフサイクル |
| :— | :— | :— |
| System | レジストリ(`HKLM\System\CurrentControlSet\Control\Session Manager\Environment`) | 恒久的(システム全体) |
| User | レジストリ(`HKCU\Environment`) | 恒久的(ユーザー個別) |
| Process | 各プロセスのPEB(Process Environment Block) | プロセス終了時に消滅 |
| Volatile | レジストリ(`HKCU\Volatile Environment`) | サインアウト(ログオフ)時に消滅 |

Volatile環境変数の物理的実体

`Volatile` 環境変数の実体は、Windowsレジストリの以下のキー配下に展開される動的エントリです。

HKEY_CURRENT_USER\Volatile Environment

このキーは、ユーザーがWindowsにサインインした際にOSによって動的に生成され、ユーザーがサインアウト(ログオフ)した瞬間に、OSによってレジストリハイブごと完全に破棄されます。

ディスク上のレジストリファイル(`NTUSER.DAT`)に永続的にフラッシュされないため、マシンの予期せぬ電源断や強制シャットダウン時にも、ディスク上に痕跡を残さない特性を持ちます。これが「揮発性(Volatile)」と呼ばれる所以です。

アーキテクチャ上の警告:セキュリティ境界としての限界

ここで、チーフアーキテクトとして最も重要な警告を述べます。

「Volatile環境変数は、暗号学的・権限的なセキュリティ境界ではない」

同一のログオンセッション内で実行されているプロセス(例:同じユーザー権限で動作する他のスクリプト、ブラウザ、一般アプリケーション)は、API(`GetEnvironmentVariable`)やレジストリシークを通じて、この `Volatile Environment` に容易にアクセスできます。

したがって、ここに平文のパスワードやマスターキーをそのまま格納することは絶対に避けてください。
本手法は、あくまで「ディスクへの永続化を防ぐこと」および「同一セッション内でのプロセス間連携における利便性向上」を目的とし、格納する値自体は「使い捨ての一時トークン」にするか、後述する「暗号化されたデータ」である必要があります。

—

2. 極限のVBScript実装:Volatileへの安全な書き込みと明示的破棄

VBScript(WSH)環境において、COMオブジェクト(`WScript.Shell`)のインスタンスはメモリリークの温床となり得ます。特にループ処理や長期稼働するサービス風のスクリプトでは、オブジェクトの明示的な解放(`Nothing` の代入)とエラーハンドリングが必須です。

以下に、実務に耐えうる堅牢なラッパー関数を含むVBScriptの実装例を示します。

実装コード(VBScript)

Option Explicit

‘ ==============================================================================
‘ メイン処理
‘ ==============================================================================
Sub Main()
Dim tokenName, secureToken
tokenName = “APP_SESSION_TEMP_TOKEN”
secureToken = “Tkn_9a8f7e6d5c4b3a2_TemporarySessionKey” ‘ ※実際には暗号化された文字列を推奨

WScript.Echo “[INFO] Volatile環境変数へのトークン書き込みを開始します…”

‘ Volatile環境変数への書き込み
If WriteVolatileEnv(tokenName, secureToken) Then
WScript.Echo “[SUCCESS] トークンは正常にVolatile領域に保持されました。”
WScript.Echo ” レジストリ [HKCU\Volatile Environment] で確認可能です。”
Else
WScript.Echo “[ERROR] トークンの保持に失敗しました。”
WScript.Quit 1
End If

‘ 同一セッション内での読み出しテスト
Dim retrievedToken
retrievedToken = ReadVolatileEnv(tokenName)
WScript.Echo “[INFO] 読み出されたトークン: ” & retrievedToken

‘ 後続プロセスが読み出せるよう、ここでは値を消去せずに終了します。
‘ ※不要になったタイミング、または処理の終端では DeleteVolatileEnv を呼んでください。
End Sub

‘ ==============================================================================
‘ ユーティリティ関数群(堅牢化モデル)
‘ ==============================================================================

”
‘ Volatile環境変数に値を書き込む
‘ @param String key 環境変数名
‘ @param String value 設定する値
‘ @return Boolean 成功時 True
‘
Function WriteVolatileEnv(ByVal key, ByVal value)
WriteVolatileEnv = False
On Error Resume Next

Dim objShell, objEnv
Set objShell = CreateObject(“WScript.Shell”)
If Err.Number <> 0 Then Exit Function

‘ “Volatile” 環境変数ターゲットを指定
Set objEnv = objShell.Environment(“Volatile”)
If Err.Number <> 0 Then
Set objShell = Nothing
Exit Function
End If

‘ 値の設定
objEnv(key) = value

If Err.Number = 0 Then
WriteVolatileEnv = True
End If

‘ COMオブジェクトの厳密なライフサイクル管理(メモリリーク防止)
Set objEnv = Nothing
Set objShell = Nothing
On Error GoTo 0
End Function

”
‘ Volatile環境変数から値を取得する
‘ @param String key 環境変数名
‘ @return String 取得した値(失敗時、または存在しない場合は空文字)
‘
Function ReadVolatileEnv(ByVal key)
ReadVolatileEnv = “”
On Error Resume Next

Dim objShell, objEnv
Set objShell = CreateObject(“WScript.Shell”)
If Err.Number <> 0 Then Exit Function

Set objEnv = objShell.Environment(“Volatile”)
If Err.Number <> 0 Then
Set objShell = Nothing
Exit Function
End If

ReadVolatileEnv = objEnv(key)

Set objEnv = Nothing
Set objShell = Nothing
On Error GoTo 0
End Function

”
‘ Volatile環境変数を明示的に削除する(クリーンアップ用)
‘ @param String key 環境変数名
‘ @return Boolean 成功時 True
‘
Function DeleteVolatileEnv(ByVal key)
DeleteVolatileEnv = False
On Error Resume Next

Dim objShell, objEnv
Set objShell = CreateObject(“WScript.Shell”)
If Err.Number <> 0 Then Exit Function

Set objEnv = objShell.Environment(“Volatile”)
If Err.Number <> 0 Then
Set objShell = Nothing
Exit Function
End If

‘ 環境変数の削除
objEnv.Remove key

If Err.Number = 0 Then
DeleteVolatileEnv = True
End If

Set objEnv = Nothing
Set objShell = Nothing
On Error GoTo 0
End Function

‘ エントリポイント実行
Main

—

3. 実務運用シナリオ:VBAからVolatile経由でPowerShellへトークンを引き渡す

このテクニックが真価を発揮するのは、「Excel VBAでユーザーに入力させた、またはWeb APIから取得した一時セッションキーを、非同期でバックグラウンド実行するPowerShellスクリプトへ安全に引き渡す」といった、マルチランタイム連携のシナリオです。

1. VBA側(送信元)の実装

ExcelマクロやAccessから、起動用PowerShellに対する「引数」としてトークンを渡すと、プロセス一覧(タスクマネージャーや `wmic process`、イベントビューアのプロセス作成ログ 4688)に平文のトークンが露出します。これを防ぐためにVolatile環境変数を利用します。

Sub DispatchToPowerShell()
Dim shellObj As Object
Dim volatileEnv As Object
Dim tokenName As String
Dim secureToken As String

tokenName = “VBA_TO_PS_TOKEN”
‘ 例: APIから取得した一時的なワンタイムトークン
secureToken = “OT_8923741982739182739182”

Set shellObj = CreateObject(“WScript.Shell”)
Set volatileEnv = shellObj.Environment(“Volatile”)

‘ Volatile領域にトークンをセット(引数として渡さない!)
volatileEnv(tokenName) = secureToken

‘ PowerShellをバックグラウンドで起動(引数にはスクリプトパスのみを指定)
Dim psCommand As String
psCommand = “powershell.exe -NoProfile -WindowStyle Hidden -File “”C:\Scripts\FetchData.ps1″””
shellObj.Run psCommand, 0, False

‘ COMオブジェクトの解放
Set volatileEnv = Nothing
Set shellObj = Nothing
End Sub

2. PowerShell側(受信元:`FetchData.ps1`)の実装

PowerShell側では、Windows APIを通じてプロセスの起動元セッションから環境変数を引き当てます。PowerShellの `$env:` プロバイダは、プロセスの初期化時に親プロセスから引き継いだ環境変数を参照しますが、`Volatile` ターゲットはレジストリ直結であるため、.NET Frameworkの機能を用いて確実に取得できます。

FetchData.ps1
——————————————————————————
安全なトークン取得プロトコル
——————————————————————————

$tokenName = “VBA_TO_PS_TOKEN”

[System.EnvironmentVariableTarget]::User や Machine ではなく
レジストリのVolatile領域から直接取得を試みる
$volatileKeyPath = “HKCU:\Volatile Environment”

if (Test-Path $volatileKeyPath) {
$token = (Get-ItemProperty -Path $volatileKeyPath -Name $tokenName -ErrorAction SilentlyContinue).$tokenName
}

if ([string]::IsNullOrEmpty($token)) {
Write-Error “必要な一時セッションキーがVolatile環境変数内に存在しません。”
Exit 1
}

try {
# ————————————————————————–
# ここでトークンを使用したAPIリクエスト等の処理を実行
# ————————————————————————–
Write-Output “トークンの取得に成功しました。処理を開始します。”
# 例: Invoke-RestMethod -Headers @{ Authorization = “Bearer $token” } …

}
finally {
# ————————————————————————–
# 使用済みの使い捨てトークンは、用が済み次第速やかに削除(最小権限・最小生存期間の原則)
# ————————————————————————–
if (Test-Path $volatileKeyPath) {
Remove-ItemProperty -Path $volatileKeyPath -Name $tokenName -ErrorAction SilentlyContinue
}
}

—

4. アーキテクトが施すべき「もう一歩進んだ」防衛策

Volatile環境変数は「ディスク永続化の回避」には極めて有効ですが、同一ユーザーセッションの他プロセスからの覗き見を完全に防ぐことはできません。ミッションクリティカルな環境、あるいは厳格なセキュリティ監査が求められるシステムでは、以下の「ハイブリッド・アプローチ」を推奨します。

DPAPI(Data Protection API)による事前暗号化

Windowsが提供する `CryptProtectData`(DPAPI)を利用し、呼び出し元ユーザーの資格情報でトークンを暗号化したバイナリ(またはBase64文字列)をVolatile環境変数に格納します。

この場合、同一セッション内の別プロセスであっても、別ユーザー権限で動作するプロセス(サービス等)はこれを復号できません。また、メモリダンプや一時レジストリスキャンに対しても強力な耐性を持ちます。

設計図:DPAPIとVolatileの連携

[VBA / VBScript]
│
▼ (DPAPIで暗号化: CryptProtectData)
[暗号化されたBase64文字列]
│
▼ (WScript.Shell.Environment(“Volatile”) に書き込み)
[レジストリ: HKCU\Volatile Environment]
│
▼ (PowerShell 等が読み出し)
[暗号化されたBase64文字列]
│
▼ (DPAPIで復号: CryptUnprotectData)
[平文トークン] ──> メモリ上でのみ使用、即破棄

このように、Windowsが標準で備えている「揮発特性」と「OSレベルの暗号化」を組み合わせることで、サードパーティ製ライブラリを一切追加することなく、エンタープライズの要求に耐えうる堅牢な一時データ連携パイプラインが完成します。

5. 結論

WSH(VBScript)やVBAといったレガシー技術を保守・運用するにあたり、現代のセキュリティ基準に適合させるための工夫はエンジニアの義務です。

`WScript.Shell` の `Volatile` 環境変数は、正しくその特性(サインアウト時の自動消滅、非永続性)を理解し、COMオブジェクトの適切な解放処理を施すことで、レガシーとモダン(PowerShell/C#)を繋ぐ極めて低レイテンシかつクリーンな「架け橋」となります。本稿に示したエラーハンドリングとライフサイクル管理を、ぜひ皆様のコードベースに適用してください。

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