Are there any implicit assumptions in Import-Clixml when importing credentials?

自作多情 提交于 2020-02-22 08:01:28

问题


I wonder if there are any implicit assumptions that I've taken that may make the code malfunction?

There is a reason I want to avoid using Import-Clixml cmdlet? Hence, I've developed an alternative, i.e. a sequence of command that is aimed to extract username and password from CliXml file created with Export-Clixml. It works by now but I'm not sure if for instance the splitting solution is reliable.

$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

May you take a glimpse and advise if there are any assumptions that make the code unreliable?


回答1:


Your approach only works in PowerShell Core on Unix-like platforms (macOS, Linux), but it shouldn't be used there for security reasons - it doesn't work on Windows (neither in Windows PowerShell nor in PowerShell Core), because the passwords there are - sensibly - truly encrypted, whereas your code assumes non-encrypted password storage.

Security Warning:

  • [securestring] on Unix-like platforms offers NO protection - the characters are stored unencrypted - the encryption underlying [securestring] on Windows only relies on the Windows-only DPAPI (Data Protection API).

    • See this Roslyn analyzer recommendation.
  • If you save a [securestring] instance to a file via Export-CliXml on a Unix-like platform - e.g. with Get-Credential | Export-CliXml MyCredentials.xml - the "secure" data (password) can trivially be retrieved by anyone who can read the file. By contrast, on Windows a DPAPI-encrypted representation is stored that can only be decrypted by the same user on the same machine.

    • As your code demonstrates, on Unix a persisted [securestring] instance is simply a "byte string" that contains the Unicode code points of the characters making up the plain-text content; for instance, a [securestring] containing string 'test' is persisted as '7400650073007400', which can be constructed as follows:

      • -join [Text.Encoding]::Unicode.GetBytes('test').ForEach({ $_.Tostring('x2') })

      • ...and converted back with:
        [Text.Encoding]::Unicode.GetString([byte[]] ('7400650073007400' -split '(..)' -ne '' -replace '^', '0x'))

In short: On Unix-like platforms (PowerShell Core), do NOT use Get-Credential | Export-CliXml to persist credentials - they will be stored UNENCRYPTED. To provide any protection at all you'd have to deny everyone else read access to the file via file permissions.


For use on Windows only, if you do need to avoid Import-CliXml, here's a greatly simplified solution that should also perform better.

While this code would technically also work on Unix-like platforms, it offers no protection whatsoever, as discussed above.

Do note that it requires the use of the ConvertTo-SecureString cmdlet in order to convert the DPAPI-encrypted password representation in the CLIXML file to a secure string ([securestring] instance).

# 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.


来源:https://stackoverflow.com/questions/55782846/are-there-any-implicit-assumptions-in-import-clixml-when-importing-credentials

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!