Encrypt passwords in powershell scripts

2 Flares Twitter 0 Facebook 0 Google+ 0 LinkedIn 2 Email -- 2 Flares ×

In powershell, in order to use credentials to authenticate against different systems you have different options. When running scripts interactively, we can configure the powershell command to ask us for username and password, but saving passwords in clear text into a script is a bad security practice. Powershell however has a way to hide passwords in commands and scripts.

Interactive authentication

I’m taking here the example of the connection to a VMware vCenter server, but remember this method can be applied to any cmdlet that support the -Credential option. Whenever you connect to vCenter, the string you execute is something like this:

Add-PSSnapin VMware.VimAutomation.Core
Connect-VIServer vcenter.skunkworks.local

Powershell asks then for username and password:

Powershell interactive

The bad bad way: passwords in the script

In a script, there’s obviously no possibility to interact with powershell, so we need to store the credentials somewhere, so they can be used automatically by powershell. The easiest and really most unsecure way to do so is to write them directly into the powershell command:

Connect-VIServer vcenter.skunkworks.local -User administrator@vsphere.local -Password password

Easy, but really dangerous as anyone can open the script and immediately get access to the password we are using. As many scripts in order to complete their tasks use high privileges users, this is really a bad security practice.

Hopefully there are ways in Powershell to encrypt passwords. The first option is to use Microsoft DPAPI (Windows Data Protection API), but I’m not going to explain this option here: it works only for scripts executed by the same user into the same machine as when they were created. As many times (and I hope you do the same) I login in a system with my own domain account, but then I run scripts using other credentials and even on different servers, this method would be useless.

Use AES to encrypt passwords

Powershell has a second method to encrypt passwords: it’s called Key/SecureKey, and uses the Advanced Encryption Standard (AES) encryption algorithm. With this method, we are able to use the stored credential from any machine with any user as long as we know the AES Key that was used. This is a great method to store scripts centrally and use them across the entire environment, without having to write the credentials into the script itself.

Let’s see how it works. A basic command to encrypt a password into a file is like this:

$File = "\\dc01\Share\powershell\AESpassword.txt"
[Byte[]] $key = (1..16)
$Password = "password" | ConvertTo-SecureString -AsPlainText -Force
$Password | ConvertFrom-SecureString -key $key | Out-File $File

If we open the txt file stored in the share, its content is:

76492d1116743f0423413b16050a5345MgB8ADIAUABxAFYANQBTAC8AbwB1AG0AbwBWAFUAUgArAFEAYwAxADcAaQBkAFEAPQA9AHwAMwA2AGQAYgA3ADIAZAA1ADUAOABhADIANABhADkAYQBmADcAYQA3ADYAOQA0ADAAMQAzAGMAOQA1ADkANABiADUAOQA1AGYAZgA5AGYANgA4AGMAYgAzAGQAYQAxAGMAMwAzADEAZgAxADQANAA0AGYAOQA5ADQAMQA4AGMANQA=

This is the encrypted version of the “password” clear text, encrypted using a 16-bit array of ascending numbers. This example is just to show you how the AES encryption works in Powershell, but is NOT to be used: since the key is stored again in the script, is basically like storing the clear password. Whoever read the script can use the same key to decrypt the encrypted password.

What you can do, is to create a random 32-bit key (the maximum supported by AES) and store it to a file:

$KeyFile = "\\dc01\Share\powershell\AES.key"
$Key = New-Object Byte[] 32
[Security.Cryptography.RNGCryptoServiceProvider]::Create().GetBytes($Key)
$Key | out-file $KeyFile

Then, invoking the stored key, create the encrypted password:

$PasswordFile = "\\dc01\Share\powershell\AESpassword.txt"
$KeyFile = "\\dc01\Share\powershell\AES.key"
$Key = Get-Content $KeyFile
$Password = "password" | ConvertTo-SecureString -AsPlainText -Force
$Password | ConvertFrom-SecureString -key $Key | Out-File $PasswordFile

When you want to connect to vCenter, the script will be like this:

 

Add-PSSnapin VMware.VimAutomation.Core

#Variables
$vcenter = "vcenter.skunkworks.local"

#Credentials
$User = "administrator@vsphere.local"
$PasswordFile = "\\dc01\Share\powershell\AESpassword.txt"
$KeyFile = "\\dc01\Share\powershell\AES.key"
$key = Get-Content $KeyFile
$VIcred = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $User, (Get-Content $PasswordFile | ConvertTo-SecureString -Key $key)

Connect-VIServer $vcenter -Credential $VIcred

Obviously, still whoever that can open the password file and the AES key can decrypt the password, but this is a necessary evil of AES: since it’s a symmetric encryption algorithm, the same key can encrypt and decrypt a clear text. But at least, we can apply strict NTFS permissions to the file, and filter incoming connections to the share, and also have audit logs applied to these two files.

2 Flares Twitter 0 Facebook 0 Google+ 0 LinkedIn 2 Email -- 2 Flares ×
  • Sam Jose

    I had created a PSCredential with the inline script and was able to access the credential until the PowerShell session which created the credential was open. When I have closed and reopened PowerShell to run the script again, I was not able to access $TFSAdminCred:
    $UserID = “xyz”
    $PswdFile = “\Server1TFSencrypt$Pswd.txt”
    $KeyFile = “\Server1TFSencrypt$AES.txt”
    $Key = New-Object byte[] 16
    [System.Security.Cryptography.RNGCryptoServiceProvider]::Create().GetBytes($Key)
    $Key | Out-File $KeyFile
    $Key = Get-Content $KeyFile
    $Pswd = “password” | ConvertTo-SecureString -AsPlainText -Force |
    ConvertFrom-SecureString -Key $Key | Out-File $PswdFile
    $TFSAdminCred = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList

  • Sypa

    Thx for your post !