非PowerShell会话启动脚本时,如何传递SecureString或PSCredential参数?
问题原因
当使用powershell.exe -File启动脚本时,外部进程的命令行参数只能以明文字符串形式传递,无法直接传递SecureString这类.NET对象。你尝试的powershell.exe -File script.ps1 -Password (Read-Host -AsSecureString)中,括号里的表达式并不会被执行——新启动的PowerShell进程会把(Read-Host -AsSecureString)当作普通字符串解析,和脚本要求的SecureString类型不匹配,因此失效。
而在PowerShell会话内用& ./script.ps1 -Password (Read-Host -AsSecureString)能正常运行,是因为此时表达式在同一个进程内执行,直接生成SecureString对象并传递,无需经过命令行字符串解析。
无需明文存储密码的解决方案
方案1:改用-Command参数启动脚本
用-Command替代-File,让新PowerShell进程执行完整的脚本逻辑,这样就能正确解析生成SecureString的表达式:
powershell.exe -Command "& './script.ps1' -Password (Read-Host -AsSecureString)"
方案2:改用PSCredential作为参数(推荐)
PSCredential是PowerShell处理身份验证的标准对象,整合了用户名和密码,比单独的SecureString更规范。修改脚本参数:
Param([Parameter(Mandatory)][PSCredential] $Credential) # 如需获取明文密码(谨慎使用):$Credential.GetNetworkCredential().Password # 多数场景可直接使用$Credential对象,比如传递给需要身份验证的Cmdlet
- 交互启动:直接运行
powershell.exe -File script.ps1,PowerShell会自动弹出用户名+密码的交互式输入框 - 非交互传递:用
-Command生成PSCredential后传递
# 交互式生成Credential powershell.exe -Command "& './script.ps1' -Credential (Get-Credential -UserName '你的用户名')" # 用已有SecureString构造Credential powershell.exe -Command "& './script.ps1' -Credential (New-Object System.Management.Automation.PSCredential('你的用户名', (Read-Host -AsSecureString)))"
方案3:加密存储密码到文件
先在PowerShell会话内生成加密的密码文件(仅当前用户/机器可解密):
Read-Host -AsSecureString | ConvertFrom-SecureString | Out-File 'encryptedPassword.txt'
修改脚本自动读取解密:
Param( [Parameter(Mandatory=$false)][SecureString] $Password ) # 未传密码时读取加密文件解密 if (-not $Password) { $Password = Get-Content 'encryptedPassword.txt' | ConvertTo-SecureString } # do some stuffs
启动时无需手动传密码,脚本会自动读取解密后的SecureString。
总结
不需要用明文字符串存储密码,通过上述方案即可在命令行启动脚本时安全传递敏感凭据。其中PSCredential是最符合PowerShell安全规范的选择,推荐优先使用。
内容的提问来源于stack exchange,提问作者Silex

