コンテンツへスキップ

ログイン時にユーザーのドキュメントにファイルをコピーする。バッチファイルに書き込むネットワーク認証のパスワードを暗号化する。

  • PC のユーザーがログインしたときに、それぞれのユーザーフォルダに設定ファイルなどをコピーしたい。
  • ログインユーザーが変わっても、各ユーザーのドキュメントフォルダに切り替えてファイルをコピーするバッチファイルを作りたい。
  • ネットワークドライブの割り当ても行うので、パスワードを暗号で書き込みたい。

ログイン時にユーザーのドキュメントにファイルをコピーする。

Contents

フォルダパスが各ユーザー毎で異なる問題

ユーザーのドキュメントフォルダって、フォルダ名に「ユーザー名」のフォルダが使われて、区別されているので、ログインしたユーザーに合わせてパスを変えないといけない問題。

PC の特定のパスは環境変数として呼び出せるようになっており、ユーザーフォルダは「%USERPROFILE%」として呼び出せます。あと、アプリケーションがキャッシュファイルをよくおいているAppdata\Roming フォルダも「%APPDATA%」で呼び出せます。

@echo off
echo Copying data from \\test-server to the user's Documents folder...

REM ユーザーのドキュメントフォルダーを取得
set "DocumentsPath=%USERPROFILE%\Documents"

REM コピー元のサーバーパス
set "SourcePath=\\test-server"

REM robocopyを使用してデータをコピー
robocopy "%SourcePath%" "%DocumentsPath%" /E /COPY:DAT /R:3 /W:3 /Z /NP

echo Copy completed successfully!
pause

ちなみに各ユーザーで共有される場所もあって、
“C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Startup” は共有のスタートアップフォルダ、
パブリックのデスクトップに置いておくと、各ユーザーのデスクトップに反映されるそうです。
(※確かにショートカットのパスを調べると、パブリックのデスクトップが置き場所になってたりする。)

ユーザーログイン時にファイルを書き込む処理

ログイン時にドキュメントへファイルを書き込む処理は、タスクスケジューラーを使用。
タスクスケジューラだと、管理者権限を使って実行したり、バッチを順番に実行するなどの設定が手軽に設定できるので。

タスクスケジューラ

全般タブで、実行するユーザーを「Administrators」にします。

最初、Administrator にしていたら、他のユーザーがログインしたときにタスクは実行はされてるんだけど、
操作に指定したバッチファイルが実行されず、一日悩んだ。

タスクの作成で、トリガーを「ログイン時:任意のユーザー」に設定。

操作で、バッチファイルを指定。

後は条件とか、設定を環境に合わせて修正。

タスクスケジューラの他にも、

  • ポリシーからログインスクリプトを設定する方方法
  • “C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Startup” バッチファイルを置いておく方法

が、考えられます。
タスクスケジューラがうまくいかなかった場合は、上記を検討しましょう。

バッチファイルに書き込むネットワーク認証のパスワードを暗号化する。

ネットワーク認証でパスワードを暗号化したい

このコマンドで「password」の部分が平文だと、セキュリティ上の問題があるので、
パスワードは暗号文で書きたい。

net use Z: \\server\share /user:username password

まあ、今回の方法で全く復号されないか?といわれたら、する方法はあるので(一時的とはいえ、環境変数に格納しているし、、、)セキュアといっても中の下くらいだと思うけど、平文のパスワードを放置するよりはマシ。

ハッシュ値を使うようなパターンは、ユーザーがパスワードを入力することが前提のお話で、
必ずしも復号して値をとりだす必要が無い場合。
今回の要件では、復号して値を取り出せることが条件になるので仕方ないっす。

処理にもよるんですが、認証が必要な処理がPowerShell でも書けるのなら、環境変数を使わないでそのまま認証しちゃえば、もっとセキュアでしょう。

まあ、コードが理解できれば結局はパスワードが見えちゃうので、ログインされる端末自体への対策や、認証につかうユーザーのアクセス権を最小限にするとかもあわせてやりましょう。

暗号化

「SecureString」を使用する

暗号化する方法は、PowerShell を使ってたものが手軽。
デフォルトでは、「PC 内部のKEY」 をつかって暗号化しているので、そのままだと違うPC では復号できない。「-KEY」 を指定して、異なる環境でも復号できるようにしてます。

$Password = "Secure_password1111"
$SecurePassword = ConvertTo-SecureString $Password -AsPlainText -Force
$EncryptedPassword = $SecurePassword | ConvertFrom-SecureString -key (1..16)
Write-Output $EncryptedPassword

こんな感じで暗号文が生成される。(powershell による、パスワードの暗号化

本来は「SecureString」はメモリの中で扱うものですが、出力してバッチファイルに記述します。

ここで出力した文字列を「暗号化したパスワード」として、バッチファイルに記述します。
これで、バッチファイルを開けばパスワードがわかる、という状況よりはマシになるでしょう。

復号して認証する

SecureString に格納されたデータは、暗号化解除を行う専用メソッド(例えば 「Marshal.SecureStringToBSTR」 など)を使用しなければ利用できないので、バッチファイルから実行する場合、PowerShell を呼び出して処理する必要があります。

先に暗号化した文字列をバッチファイル内で呼び出したPowerShell で復元し、環境変数へ入れて認証に使用します。

powershell -NoProfile -ExecutionPolicy Bypass -Command ^
    "$EncryptedString='01000000d08c9ddf0115d1118c7a00c04fc297eb010000000ff99a183592da449a41d656f7061f18000000000200000000001066000000010000200000004459d2464d92cd510dcccf61fc081b8cdd04509bc57cbe4d961204c247f7368b000000000e80000000020000200000007176a35e5cb71fc284c124d34f5ba8cf06754690253907a239dab08daae9a0b330000000e62e0a03037992877eac262fa98aacc4a8852da850100da7004b5cdf467246240a9624715aefc415a4e39f55e011367640000000dbe5c276d4a1d9ad5a105bbdd73d2e6b017d630c7cd200908d4b0cf913e52ace5fed56177c5f22a6b0a61f5a58b7c09c3231d0e48352eaabc503afea473526b7';" ^
    "$SecurePassword=$EncryptedString | ConvertTo-SecureString;" ^
    "$BSTR=[System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($SecurePassword);" ^
    "$PlainTextPassword=[System.Runtime.InteropServices.Marshal]::PtrToStringAuto($BSTR);" ^
    "[System.Runtime.InteropServices.Marshal]::ZeroFreeBSTR($BSTR);" ^
    "[Environment]::SetEnvironmentVariable('MY_SECURE_PASSWORD', $PlainTextPassword, 'User');"

SecureString を復号

SecureString を復号して平文の値に変換します。

powershell

$BSTR = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($SecurePassword)
$PlainTextPassword = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto($BSTR)

環境変数に設定して認証時に使用する

上の例で、環境変数に値を入れているのは、このくだり。
この時点では、平文で環境変数に書き込まれますので注意が必要です。

"[Environment]::SetEnvironmentVariable('MY_SECURE_PASSWORD', $PlainTextPassword, 'User');"

ネットワーク認証をする行ははこんな感じで、パスワードを入力した環境変数を使用しています。
(これしか方法が浮かばなかっただけなので、他にも良い方法があると思う。)この環境変数は認証後に消します。

net use X: '\\test-server' /user:Administrator %MY_SECURE_PASSWORD%

環境変数を設定するには、
[Environment]::SetEnvironmentVariable メソッドを使用します。

powershell

[Environment]::SetEnvironmentVariable("MY_SECURE_PASSWORD", $PlainTextPassword, "User")
  • “User” を指定すると、現在のユーザースコープに環境変数が設定されます。
  • “Machine” を指定すると、システム全体のスコープに設定されます(管理者権限が必要)。

メモリ解放(セキュリティ対策)

SecureString を復号した後のデータを解放して、メモリ上に情報が残らないようにします

powershell

[System.Runtime.InteropServices.Marshal]::ZeroFreeBSTR($BSTR)

環境変数が設定されたか、またその中身を確認する場合は、以下を使用。

powershell

Write-Host "Environment Variable: $env:MY_SECURE_PASSWORD"

最後に以下のようなコードを実行して、環境変数を削除します。

powershell

powershell -NoProfile -ExecutionPolicy Bypass -Command "[Environment]::SetEnvironmentVariable('MY_SECURE_PASSWORD','', 'User');"

注意点

  1. セキュリティリスク:
    • SecureString をプレーンテキストに復号すると、セキュリティが低下します。そのため、復号したデータは必要最低限の操作後すぐに破棄してください(例:Marshal.ZeroFreeBSTR を使用)。
  2. 相互運用性:
    • 他の言語やフレームワークでも SecureString を活用する場合、その環境が .NET に依存している必要があります。

【メモ】環境変数を使わずに、Powershell でそのまま認証まで行うのもあり。

処理にもよるんですが、認証が必要な処理がPowerShell でも書けるのなら、環境変数を使わないでそのまま認証しちぇば、もっとセキュアでしょう。 今回はネットワークドライブへの接続だったので、そのままPowerShell で割り当てしても良かったかなーと思います。

まあ、コードが理解できれば結局はパスワードが見えちゃうので、ログインされる端末自体への対策や、認証につかうユーザーのアクセス権を最小限にするとかもあわせてやりましょう。

事前にユーザー名とパスワードを指定する例
# パスワードをセキュア形式に変換
$securePassword = ConvertTo-SecureString "YourSecurePassword" -AsPlainText -Force

# ユーザー名とセキュアなパスワードでPSCredentialオブジェクトを作成
$credential = New-Object System.Management.Automation.PSCredential("YourUsername", $securePassword)

# 内容を確認(暗号化されているのでパスワードは読めません)
$credential
使用例: ネットワークドライブ接続

作成した資格情報を使ってネットワークドライブを接続します。

New-PSDrive -Name "Z" -PSProvider FileSystem -Root "\\ServerName\SharedFolder" -Credential $credential -Persist
上記を使用して、ネットワークドライブを作成する例

上記を使用して、バッチファイルに暗号化したパスワードを書き込んで使う場合、一度復号してからSecureString に入れる感じになるので、こんな感じになりました。

$EncryptedString="76492d1116743f0423413b16050a5345MgB8AHQAaQBpAEEAMQAvAFMAaABzAHIAOQB5AHkAcwBTAGYANgBQADEAZgBkAEEAPQA9AHwAYQA3ADkAYwA1ADEANQAwAGEAMgA5AGUANgAwAGQAMQA1AGEANgA5ADkAZAA4AGMAOAA0AGYAZABhADEAMAA4AA=="

# パスワードを復号
$SecurePassword0 = ConvertTo-SecureString  -key (1..16) $EncryptedString
$BSTR=[System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($SecurePassword0)
$PlainTextPassword=[System.Runtime.InteropServices.Marshal]::PtrToStringAuto($BSTR)

# メモリからクリア
[System.Runtime.InteropServices.Marshal]::ZeroFreeBSTR($BSTR);

# パスワードをセキュア形式に変換
$securePassword = ConvertTo-SecureString $PlainTextPassword -AsPlainText -Force

# ユーザー名とセキュアなパスワードでPSCredentialオブジェクトを作成
$credential = New-Object System.Management.Automation.PSCredential("Administrator", $securePassword)

# 内容を確認(暗号化されているのでパスワードは読めません)
$credential

# ネットワークドライブを割り当て
New-PSDrive -Name "X" -PSProvider FileSystem -Root "\\test-server\Share" -Credential $credential -Persist -Scope Global

ドライブの割り当ての時、「-Persist」、「-Scope Global」の引数をつけておかないと、エクスプローラーから見えないです。 

参考(コパイロット調べ)

コパイロットさんに聞いた時の回答をまとめ。
ちなみにアイコンは、Bing時代に自分で自分のイメージ画を生成したものの中から、イメージがそれっぽいものを選んでます。

powershell による、パスワードの暗号化

パスワード「test」がプレーンテキスト形式で変数 $Password に格納して、
次に平文のパスワード $Password を、SecureString 形式に変換しています。

$Password = "test"
$SecurePassword = ConvertTo-SecureString $Password -AsPlainText -Force
$EncryptedPassword = $SecurePassword | ConvertFrom-SecureString
Write-Output $EncryptedPassword

ConvertFrom-SecureString を使って、文字列として扱えるようにします。

> write-output $SecurePassword
System.Security.SecureString

> write-output $SecurePassword | ConvertFrom-SecureString
01000000d08c9ddf0115d1118c7a00c04fc297eb010000000ff99a183592da449a41d656f7061f1800000000020000000000106600000001000020000000ae835cd10931be85348e5fc91013bf8d4f9e089d0bdea38b461db06ec8ec7e65000000000e80000000020000200000009c70dc72f0f0c297f4bde6b405cf6b7897924b531e08341524d150298ac287c5100000009d20a85f6f5650a6aa8064e9f7176d2c4000000096a1d8e9a6055eac59b8569db0f37b92e2deffb24e237e51e4def2011e53d3b89d46b011fe34a86323cb8f9913404bd69d5bc30fc5979c75842e92ad2e51a138

このコードは、PowerShellで平文(プレーンテキスト)のパスワードを「セキュアな形式」に変換する処理を行っています。それぞれの部分を詳しく説明します:

  1. ConvertTo-SecureString
    • プレーンテキストや他の形式の文字列を SecureString に変換するPowerShellのコマンドレット。
  2. -AsPlainText
    • 変換元が平文であることを指定します。
    • 通常、セキュリティ上平文のパスワードを使用するのは避けられるべきですが、このオプションを付与することで意図的に処理が許可されます。
  3. -Force
    • 平文の文字列を変換する際、確認プロンプトなしで処理を強制的に実行します。
    • PowerShellでは通常、平文からSecureStringに変換する操作は警告や確認が必要ですが、-Force を指定するとそれをスキップします。

SecureStringについて

SecureString の格納方法

SecureString はどこに格納されますか?

メモリ上の暗号化:

SecureString は、コンピュータのメモリに格納される際、システムにより暗号化されます。そのため、他のプロセスがその内容に直接アクセスすることはできません。

一般的な文字列(string 型)は暗号化されず、メモリに平文で格納されるため、セキュリティリスクが高くなります。

限定的なアクセス:

SecureString に格納されたデータは、暗号化解除を行う専用メソッド(例えば Marshal.SecureStringToBSTR など)を使用しなければ利用できません。
このような制御により、誤用や漏洩を防ぎます。

暗号化キー

システムは専用のキーを使用して SecureString を暗号化し、このキーはオペレーティングシステムによって管理されます。開発者はこのキーに直接アクセスすることはできません。

重要なポイント

SecureString の内容はディスクやファイルに保存されるわけではなく、メモリ上のみで保持されます。そのため、プログラムが終了するとメモリから消去され、情報が残りません。
この設計により、データをできる限り安全に扱うことができます。
暗号化された文字列として扱われ、より安全にパスワードや機密データを管理するための形式で、
例えば、Windows認証やリモートセッションで安全に使用できます。

プレーンテキストではなく暗号化されたデータとしてパスワードを扱うことで、安全性を向上させますが、高度な言語でないと使用できないところがネックです。

例:powershell

$Credential = New-Object System.Management.Automation.PSCredential("Username", $SecurePassword)
PowerShell以外でも使えるのか

SecureString は PowerShell 特有の概念ではなく、.NET 環境全体で利用可能です。
そのため、.NETを活用している他のアプリケーション(例えば独自のCLIツールやデスクトップアプリケーション)でも同様の操作が可能です。

[System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($SecurePassword) これはどんな処理ですか

[System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($SecurePassword) は、PowerShellで SecureString 型のデータを通常のプレーンテキスト文字列に変換するための処理です。

このメソッドは、System.Runtime.InteropServices 名前空間に属しています。以下に詳細な説明を示します:

使用例

以下は、このメソッドを使ってプレーンテキストに変換する例です:

powershell

$SecurePassword = ConvertTo-SecureString "P@ssw0rd" -AsPlainText -Force
$BSTR = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($SecurePassword)
$PlainTextPassword = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto($BSTR)

# プレーンテキストの内容を表示
Write-Host $PlainTextPassword

# メモリからBSTRを解放(重要!)
[System.Runtime.InteropServices.Marshal]::ZeroFreeBSTR($BSTR)
処理の流れ
  1. SecureString のデータ取得:
    • $SecurePassword は、暗号化された形式で管理される SecureString データです。この形式は安全性が高いですが、直接読み取ることはできません。
  2. SecureStringToBSTR の役割:
    • SecureStringToBSTR は、SecureString を暗号解除し、非セキュアな形式である「BSTR」(文字列)に変換します。
    • BSTR は、WindowsプラットフォームのCOM(Component Object Model)で使用される文字列形式です。

バッチファイルで使いたいときは?

SecureString をバッチファイルで直接扱うことはできません。これは、バッチファイルが .NET や PowerShell のような高度な型(例えば SecureString)をサポートしていないためです。しかし、PowerShell をバッチファイルから呼び出すことで、SecureString を活用する方法があります。

バッチファイルでは文字列操作は可能ですが、暗号化されたデータ型(SecureString)を管理するにはPowerShellのような高機能環境が必要です。

バッチファイルは主に、PowerShellや他のスクリプトを呼び出して統合的に管理する役割を果たします。

バッチファイルからPowerShellを呼び出す方法

バッチファイル内で単純なPowerShellコマンドを実行する例です。

バッチファイルの例

cmd

@echo off
powershell -NoProfile -ExecutionPolicy Bypass -Command "Get-Date"
pause

結果

このスクリプトを実行すると、現在の日付と時刻がPowerShellから取得されて表示されます。

(Visited 17 times, 1 visits today)

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です