适用于 PowerShell 5.1 及以上版本,SecretManagement 模块需要 PowerShell 7
脚本中的硬编码密码是安全隐患的头号来源。无论是数据库连接字符串中的密码、API 密钥还是 SSH 私钥,都应该使用安全的存储机制。PowerShell 提供了多层凭据管理方案——从基本的 PSCredential 对象到 SecureString,再到现代化的 SecretManagement 模块,可以满足从单机脚本到企业级自动化的所有需求。
本文将讲解凭据的安全创建、存储、使用,以及 Microsoft.PowerShell.SecretManagement 模块的使用。
PSCredential 基础
PSCredential 是 PowerShell 中表示用户名+密码的标准方式:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| $cred = Get-Credential -Message "输入数据库管理员凭据" Write-Host "用户名:$($cred.UserName)"
$plainPassword = "MyPassword123!" $securePassword = ConvertTo-SecureString $plainPassword -AsPlainText -Force $cred = New-Object System.Management.Automation.PSCredential("admin", $securePassword)
$password = $cred.GetNetworkCredential().Password Write-Host "密码长度:$($password.Length) 字符"
function Test-Credential { param([PSCredential]$Credential)
try { $context = New-Object System.DirectoryServices.DirectoryEntry( "", $Credential.UserName, $Credential.GetNetworkCredential().Password ) if ($context.Name) { Write-Host "凭据有效:$($Credential.UserName)" -ForegroundColor Green return $true } } catch { Write-Host "凭据无效:$($_.Exception.Message)" -ForegroundColor Red return $false } }
|
执行结果示例:
1 2 3
| 用户名:admin 密码长度:13 字符 凭据有效:CONTOSO\admin
|
安全文件存储
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
| function Save-CredentialToFile {
param( [Parameter(Mandatory)] [string]$Name,
[Parameter(Mandatory)] [PSCredential]$Credential,
[string]$Path = "$env:USERPROFILE\.creds" )
if (-not (Test-Path $Path)) { New-Item -Path $Path -ItemType Directory | Out-Null }
$filePath = Join-Path $Path "$Name.xml" $Credential | Export-Clixml -Path $filePath Write-Host "凭据已保存到:$filePath" -ForegroundColor Green }
function Get-CredentialFromFile {
param( [Parameter(Mandatory)] [string]$Name,
[string]$Path = "$env:USERPROFILE\.creds" )
$filePath = Join-Path $Path "$Name.xml" if (-not (Test-Path $filePath)) { Write-Error "凭据文件不存在:$filePath" return $null }
Import-Clixml -Path $filePath }
$dbCred = Get-Credential -Message "输入数据库凭据" Save-CredentialToFile -Name "database" -Credential $dbCred
$loadedCred = Get-CredentialFromFile -Name "database" Write-Host "已加载凭据:$($loadedCred.UserName)"
|
执行结果示例:
1 2
| 凭据已保存到:C:\Users\admin\.creds\database.xml 已加载凭据:sa
|
注意:Export-Clixml 使用 Windows DPAPI 加密,只有当前用户在同一台机器上才能解密。不要将 .creds 目录加入版本控制。
SecretManagement 模块
PowerShell 7 的 SecretManagement 模块是微软推荐的现代化凭据管理方案,支持多种后端存储:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| Install-Module -Name Microsoft.PowerShell.SecretManagement -Scope CurrentUser -Force Install-Module -Name Microsoft.PowerShell.SecretStore -Scope CurrentUser -Force
Register-SecretVault -Name "LocalVault" -ModuleName Microsoft.PowerShell.SecretStore -DefaultVault
Set-SecretStorePassword
$dbCred = Get-Credential -Message "数据库凭据" Set-Secret -Name "DBAdmin" -Secret $dbCred -Vault "LocalVault"
Set-Secret -Name "GitHubToken" -Secret "ghp_xxxxxxxxxxxx" -Vault "LocalVault" Set-Secret -Name "OpenAIKey" -Secret "sk-xxxxxxxxxxxx" -Vault "LocalVault"
Get-SecretInfo -Vault "LocalVault" | Select-Object Name, Type | Format-Table -AutoSize
$token = Get-Secret -Name "GitHubToken" -AsPlainText Write-Host "Token 长度:$($token.Length)"
$cred = Get-Secret -Name "DBAdmin" Write-Host "用户名:$($cred.UserName)"
function Connect-MyDatabase { $dbCred = Get-Secret -Name "DBAdmin" $connString = "Server=prod-db;User ID=$($dbCred.UserName);Password=$($dbCred.GetNetworkCredential().Password)" }
|
执行结果示例:
1 2 3 4 5 6 7 8
| Name Type ---- ---- DBAdmin PSCredential GitHubToken String OpenAIKey String
Token 长度:40 用户名:sa
|
Azure Key Vault 集成
对于企业环境,Azure Key Vault 是推荐的集中式密钥管理方案:
1 2 3 4 5 6 7 8 9 10 11 12 13
| Install-Module -Name Az.KeyVault -Scope CurrentUser -Force
Register-SecretVault -Name "AzureKV" -ModuleName Az.KeyVault ` -VaultParameters @{ AZKVaultName = 'my-company-vault'; SubscriptionId = 'xxx-xxx' }
$apiCred = Get-Credential -Message "API 服务账户" Set-Secret -Name "ApiServiceAccount" -Secret $apiCred -Vault "AzureKV"
$apiCred = Get-Secret -Name "ApiServiceAccount" -Vault "AzureKV"
|
执行结果示例:
环境变量传递敏感信息
在 CI/CD 和容器化环境中,环境变量是传递凭据的常用方式:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| $dbUser = $env:DB_USER $dbPass = $env:DB_PASSWORD | ConvertTo-SecureString -AsPlainText -Force $dbCred = New-Object PSCredential($dbUser, $dbPass)
Write-Host "数据库用户:$($dbCred.UserName)"
$apiKey = $env:OPENAI_API_KEY if (-not $apiKey) { Write-Error "未设置环境变量 OPENAI_API_KEY" exit 1 } Write-Host "API Key 已加载(长度:$($apiKey.Length))"
$requiredEnvVars = @('DB_USER', 'DB_PASSWORD', 'API_KEY', 'SMTP_SERVER') $missing = $requiredEnvVars | Where-Object { -not (Get-ChildItem env:$_ -ErrorAction SilentlyContinue) }
if ($missing) { Write-Error "缺少环境变量:$($missing -join ', ')" exit 1 } Write-Host "所有环境变量已配置" -ForegroundColor Green
|
执行结果示例:
1 2 3
| 数据库用户:sa API Key 已加载(长度:48) 所有环境变量已配置
|
注意事项
- 不要硬编码密码:脚本中永远不应出现明文密码。使用环境变量、密钥库或交互式输入
- DPAPI 限制:
Export-Clixml 的 DPAPI 加密绑定到当前用户和机器,不能跨机器使用
- SecretStore 密码:忘记 SecretStore 密码后无法恢复已存储的密钥,务必妥善保管
- 日志脱敏:脚本输出中不要打印密码和密钥。使用
**** 替代敏感信息
- 最小权限原则:为自动化服务账户仅授予必要的最小权限
- 定期轮换:定期轮换 API 密钥和服务账户密码,并在密钥库中同步更新