导入凭据时Import-Clixml中是否有任何隐含的假设?

问题描述 投票:2回答:1

我想知道我是否有任何隐含的假设可能导致代码故障?

我想避免使用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

您是否可以一瞥并建议是否存在使代码不可靠的假设?

powershell credentials securestring networkcredentials powershell-core
1个回答
2
投票

您的方法仅适用于类Unix平台(macOS,Linux)上的PowerShell Core,但出于安全原因不应在此处使用 - 它不适用于Windows(Windows PowerShell和PowerShell Core中都不能)。

安全警告:

  • 类似Unix的平台上的[securestring]没有提供任何保护 - 字符存储未加密 - Windows上的[securestring]加密只依赖于Windows-only DPAPI。 见this Roslyn analyzer recommendation
  • 如果在类Unix的平台上通过[securestring]Export-CliXml实例保存到文件中 - 例如使用Get-Credential | Export-CliXml MyCredentials.xml - 任何能够读取文件的人都可以轻松地检索“安全”数据(密码)。相比之下,在Windows上存储的DPAPI加密表示只能由同一台机器上的同一用户解密。 正如您的代码所示,在Unix上,持久化的[securestring]实例只是一个“字节字符串”,其中包含构成纯文本内容的字符的Unicode代码点;例如,包含字符串[securestring]'test'作为'7400650073007400'持久化,可以构造如下: -join [Text.Encoding]::Unicode.GetBytes('test').ForEach({ $_.Tostring('x2') }) ...并转换回: [Text.Encoding]::Unicode.GetString([byte[]] ('7400650073007400' -split '(..)' -ne '' -replace '^', '0x'))

简而言之:在类Unix平台(PowerShell Core)上,不要使用Get-Credential | Export-CliXml来保存凭据 - 它们将被存储为UNENCRYPTED。要提供任何保护,您必须拒绝其他人通过文件权限读取文件的访问权限。


仅在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 = [pscredential]::new(
  $username, 
  (ConvertTo-SecureString $encryptedPassword)
).GetNetworkCredential()

$networkCred.UserName
# $networkCred.Password  # CAUTION: This would echo the plain-text password.
© www.soinside.com 2019 - 2024. All rights reserved.