I'm writing PowerShell modules that execute commands or scripts over SSH on remote Windows machines, and I'm looking for a way to transmit a password (or other sensitive information) over the connection. For example, imagine that we have a PowerShell script that contains this invocation:
ssh user@host powershell -Command Invoke-Foo -Password $password
As I learned from one of my previous questions, this example demonstrates poor security—the password, even though encrypted over SSH, appears as plain text on both systems. To protect against this, scripts store sensitive data as SecureString
or PSCredential
objects:
$password = Read-Host -AsSecureString
...which works fine on the local machine, but the scripts cannot pass the SecureString
objects over the text-based SSH protocol to a remote PowerShell process.
For background: I'm automating some processes that a person needed to type directly into a PowerShell session while connected to a machine over SSH, so the need for this did not exist before.
Unfortunately, we're walled-in with PowerShell 4, so the SSH facilities provided by newer PowerShell versions are not available, and, for non-technical reasons, we cannot use WinRM-based PowerShell remoting, as much as I'd like to. This means that I cannot rely on New-PSSession
or Invoke-Command
to serialize and send local objects to a remote session.
Within these constraints, how can I design PowerShell scripts to pass the sensitive data as text through SSH, and then receive the data remotely without exposing it? Is there a way to do it without installing encryption utilities and keys on every system?
Since you can't rely on integrated Windows authentication, I don't think key management can be avoided. Depending on the security needs, how about a SecureString? It's possible to move those around - if you can tolerate shared secrets. In order to copy SecureStrings around, pre-generated key must be used. If it isn't, Windows will pick one via DPAPI - and the file won't work on other systems.
The first step is to create a SecureString and save it into a file with ConvertFrom-SecureString
. The contents are encyrpted with AES.
# Save the credential in a file
$PlainPassword = <mySecretPassword>
$SecurePassword = $PlainPassword | ConvertTo-SecureString -AsPlainText -Force
$key = (<pre-gernerate 24*8 bit values = 192 bits in an array. Keep it safe somewhere>)
$SecurePasswordKey = ConvertFrom-SecureString $SecurePassword -Key $key
set-content -path <myEncryptedFile> -input $SecurePasswordKey
Now that the Windows password is encrypted, copy the myEncryptedFile into the remote server. scp
could work, as SSH is used anyway.
Working on the remote server within a SSH session, create a PSCredential based on the encrypted file.
$key = (<Use the same pre-generated key here>)
$SecurePass = cat <myEncryptedFile>
$password = ConvertTo-SecureString -String $SecurePass -Key $key
$creds = new-object -typename System.Management.Automation.PSCredential `
-argumentlist <domain\username>, $password
Now you have a PSCredential object that contains proper credentials and can be passed around.
Moving the $key
is, sadly enough, another a problem. Maybe you can tolerate copying it in a file, like the SecureString, and delete it after invoking whatever Powershell was needed. If, and that's a mighty big if, the key is used only once and the actual credential file is kept confidential, you could pass the key as an argument.
Please be aware that this solution is likely to contain more than one security hole. For example, if you use weak keys and copy the files into world readable locations, it is easy for an attacker to decrypt the key file.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With