我想知道我是否采取了任何隐含的假设,可能会使代码出现故障?
我想避免使用 Import-Clixml cmdlet 的原因是什么? 因此,我开发了一个替代方案,即一系列命令,旨在从使用 Export-Clixml 创建的 CliXml 文件中提取用户名和密码。 它现在可以工作了,但我不确定例如拆分解决方案是否可靠。
$credFileUriBld = [UriBuilder]::New('file','localhost',-1,"MyCredentials.xml"))
$credFile = [Xml.XMLDocument]::New()
$nsMgr4ps1xml = [Xml.XmlNamespaceManager]::New($credFile.NameTable)
$nsMgr4ps1xml.AddNamespace('ps1xml','http://schemas.microsoft.com/powershell/2004/04')
$credFile.Load($credFileUriBld.Path)
$netCredInfo = [System.Net.NetworkCredential]::New($credFile.SelectSingleNode('/ps1xml:Objs/ps1xml:Obj/ps1xml:Props/ps1xml:S[@N=''UserName'']/text()',$nsMgr4ps1xml).Get_Value(),
($credFile.SelectSingleNode('/ps1xml:Objs/ps1xml:Obj/ps1xml:Props/ps1xml:SS[@N=''Password'']/text()',$nsMgr4ps1xml).Get_Value().Split('00') |
ForEach-Object { if([String]::IsNullOrEmpty($_)) { } else { $_.Trim() } } |
ForEach-Object { [convert]::ToInt32($_,16) } |
ForEach-Object { [convert]::ToChar($_) } |
ForEach-Object -Begin { $ss=[SecureString]::New() } -Process {$ss.AppendChar($_)} -End { $ss }))
$netCredInfo.UserName
$netCredInfo.Password
您可以瞥一眼并建议是否有任何假设使代码不可靠吗?
您的方法仅适用于类Unix平台(macOS,Linux)上的PowerShell Core,但出于安全原因,它不应该在那里使用- 它不适用于Windows(无论是在Windows PowerShell中还是在PowerShell Core中),因为那里的密码 - 明智地 - 真正加密,而您的代码假设未加密的密码存储。
安全警告:
-
类Unix平台上的
[securestring]
不提供任何保护 - 字符未加密存储 - Windows上的加密基础[securestring]
仅依赖于仅限Windows的DPAPI(数据保护API)。-
对于新代码,通常不建议使用
[securestring]
- 请参阅此 Roslyn 分析器建议。
-
对于新代码,通常不建议使用
-
如果您通过类Unix平台上的
Export-CliXml
将[securestring]
实例保存到文件中- 例如使用Get-Credential | Export-CliXml MyCredentials.xml
- ">安全"数据(密码)可以被任何可以读取文件的人轻松检索。 相比之下,在 Windows 上存储了 DPAPI 加密的表示形式,该表示形式只能由同一台计算机上的同一用户解密。-
正如您的代码所演示的那样,在Unix上,持久化的
[securestring]
实例只是一个"字节字符串",其中包含构成纯文本内容的字符的Unicode代码点;例如,包含字符串'test'
的[securestring]
持久化为'7400650073007400'
,可以构造如下:-
-join [Text.Encoding]::Unicode.GetBytes('test').ForEach({ $_.Tostring('x2') })
-
。并转换回来:
[Text.Encoding]::Unicode.GetString([byte[]] ('7400650073007400' -split '(..)' -ne '' -replace '^', '0x'))
-
-
简而言之:在类Unix平台(PowerShellCore)上,不要使用Get-Credential | Export-CliXml
来保存凭据- 它们将以未加密的方式存储。要提供任何保护,您必须通过文件权限拒绝其他人对文件的读取访问权限。
仅适用于Windows,如果您确实需要避免Import-CliXml
,这是一个大大简化的解决方案,也应该表现得更好。
虽然此代码在技术上也适用于类Unix平台,但如上所述,它不提供任何保护。
请注意,它需要使用ConvertTo-SecureString
cmdlet 才能将 CLIXML 文件中的 DPAPI 加密密码表示形式转换为安全字符串([securestring]
实例)。
# Load the CLIXML file into a [System.Xml.XmlDocument] ([xml]) instance.
($credXml = [xml]::new()).Load($PWD.ProviderPath + 'MyCredentials.xml')
# Take an XPath shortcut that avoids having to deal with namespaces.
# This should be safe, if you know your XML file to have been created with
# Get-Credential | Export-CliXml MyCredentials.xml
$username, $encryptedPassword =
$credXml.SelectNodes('//*[@N="UserName" or @N="Password"]').'#text'
$networkCred = [System.Net.NetworkCredential]::new(
$username,
(ConvertTo-SecureString $encryptedPassword)
)
$networkCred.UserName
# $networkCred.Password # CAUTION: This would echo the plain-text password.