Securely Store (and Retrieve) Credentials In Powershell Scripts
22 March 2019
Automating boring s**t with PowerShell is PowerShell’s whole shtick. If something happens regularly, and it’s pretty important, you should probably make it happen without bums in seats and hands on keyboards.
For this, PowerShell automation is all sunshine and rainbows. Sometimes though, you need to automate stuff that requires credentials. Maybe we can do this? 👇
$mySuperSafePassword = 'P@ssword!'
Nope. Storing your password in plain text is not only stupid, it should probably be considered a dismissable offense!
With our disclaimers out of the way, now we need to encrypt and store our password. This is the easy bit. From a PowerShell console, run the below command. You’ll be prompted to enter a username and password. Enter whatever you want in the username field, as we’re only concerned with storing passwords for this tutorial.
(Get-Credential).password | ConvertFrom-SecureString
This single-liner takes the password you entered, stored as a SecureString object, and converts it to an encrypted string using the Windows Data Protection API. In short, it encrypts the string using a key derived from:
- The password/username combination of the user running the script
- A unique device identifier of the machine that the script is running on
Store the output of the above line in your code as you wish. In this tutorial, we’re going to put it in a variable like below.
$encryptedPassword = '01000000d08c9ddf0115d1118c7a00c04fc297eb010000005b6e6f2c69930c4e9b6b1e1cdad79e3b0000000002000000000003660000c000000010000000d33c27e9bd07dab24012caf5f52a086a0000000004800000a000000010000000f17cf83e3101f8ee6da62930a3734cbd180000004e40a628f82ae2c4b90c5113d0fa64fb7b4d0c5515f809c314000000c073d2b4d97696689bdae675ba1b042831e7824e'
Now that we’ve stored our password, let’s see how we can use it. To begin with, let’s convert it to a SecureString object.
$encryptedPassword = $encryptedPassword | ConvertTo-SecureString
Taking our brand new SecureString, we use the Marshal.SecureStringToCoTaskMemUnicode method from .NET to copy the contents to a block of memory.
$pointer = [System.Runtime.InteropServices.Marshal]::SecureStringToCoTaskMemUnicode($encryptedPassword)
With that block of memory, using the Marshal.PtrToStringUni method, we convert it to a managed string to make it human-readable again.
$decryptedPassword = [System.Runtime.InteropServices.Marshal]::PtrToStringUni($pointer)
This bit is important! We need to remove the above from memory. This line below uses the Marshal.ZeroFreeCoTaskMemUnicode to zero out the pointer and free up the memory.
And that’s it! Now, using the
$decryptedPassword variable, you can do what needs to be done. Let’s tie it all together:
$encryptedPassword = '01000000d08c9ddf0115d1118c7a00c04fc297eb010000005b6e6f2c69930c4e9b6b1e1cdad79e3b0000000002000000000003660000c000000010000000d33c27e9bd07dab24012caf5f52a086a0000000004800000a000000010000000f17cf83e3101f8ee6da62930a3734cbd180000004e40a628f82ae2c4b90c5113d0fa64fb7b4d0c5515f809c314000000c073d2b4d97696689bdae675ba1b042831e7824e' $encryptedPassword = $encryptedPassword | ConvertTo-SecureString $pointer = [System.Runtime.InteropServices.Marshal]::SecureStringToCoTaskMemUnicode($encryptedPassword) $decryptedPassword = [System.Runtime.InteropServices.Marshal]::PtrToStringUni($pointer) [System.Runtime.InteropServices.Marshal]::ZeroFreeCoTaskMemUnicode($pointer) # Do something
This post was last updated on 8 October 2019