PowerShell Credentials and SecureStrings, Part III
In my first blog post on using secure passwords in PowerShell scripts, I explained how the .NET Framework uses Strings and a new object type, SecureStrings. SecureStrings have several features that protect against attacks to read string values from memory, not the least of which is that the string is encrypted from the start in memory. I then introduced the Get-PSCredentialcmdlet that will prompt for a username and password and store that password in a SecureString property, with the property incidentally named “Password”. An alternative discussed there, when only a SecureString is needed and not a full credential, was the Read-Host cmdlet with the –AsSecureString parameter.
These two cmdlets, Get-PSCredential and Read-Host work great when running scripts interactively as they prompt the user running the script for the necessary values, but will not work if you want to run the script as a schedule process. In the case of scheduling a script, the password will need to be stored with the script. The ConvertFrom-SecureString allows you to take a SecureString object and convert it to an encrypted string that can be written to a file usingSet-Content. The ConvertTo-SecureString cmdlet then can be used to convert from an encrypted string, usually read from a file, back into the SecureString to be used with PSCredential objects or cmdlets that take a SecureString directly.
ConvertFrom-SecureString offers three ways to take a SecureString and convert it to a normal encrypted String that can be written to a file. I discussed the default behavior of the cmdlet that encrypts the SecureString using the Windows data protection cryptographic API. While a very secure option, it is limited to the user that runs the cmdlet on the same computer that they encrypted it. In this post, I will discuss options for securing passwords in scripts/files that do not have that limitation and still allow you to run your PowerShell scripts on an automated schedule. This will include the other two encryption options of the ConvertFrom-SecureString.
–Key and –SecureKey Parameters
ConvertFrom-SecureString and ConvertTo-SecureString have two parameters that you can use to change the default behavior. These parameters are –SecureKey and –Key. The –SecureKey parameter takes a SecureString object and the –Key parameter, a byte array (Byte)
You use –SecureKey with a SecureString memory object that encrypt and decrypt the other SecureString. This does not really help us in our goal of storing secure passwords and accessing them through automation. You would need a SecureString object built, either by unencrypting another string using anotheroption, or by having someone type the SecureString interactively. The first does not solve the problem of it being secured, the second does not solve the automation problem.
The –Key parameter allows you to use a 128-bit (16-byte), 192-bit (24-byte), or 256-bit (32-byte) key and uses the Advanced Encryption System (AES) cipher, also known as the Rijndael cipher, to encrypt/decrypt the SecureString. It is symmetric encryption so you need to provide the same key for encryption as you do when you decrypt the encrypted string back to the SecureString. One way to do this is to embed the key in each script file. Not surprising, this not recommended and results in security not much more secure than just storing the password in plain text in the script. It also makes it difficult to change the key or password which shoulddue frequently using this method. A better choice is to store the key in a separate file from the script and encrypted password.
Use NTFS permissions to secure the file with the key so that only the users you want to use the key and access it. Even better would be to use Encrypting File System to encrypt the key file and share to only the user accounts that should be able to access it. You can then create a new file with a new key whenever you like and use the key to re-encrypt the password. Of course, re-encrypt the password to a new password whenever the password changes. To increase the security (a little, as it’s a “security by obscurity” trick), store the password file and key file in separate locations. Here is what that codemylook like (notice at the end we get a SecureString back):
You should always get a SecureString back, but if the wrong key was used, then the password will be wrong and authentication in your scripts will not work. This is exactly what you want.
Other Options to Protect Passwords
Of course, this is less secure than still other solutions. As mentioned in the 2nd blog post on this subject, 3rd party solutions can often be easier and more secure to implement. Another option is to use Public Key Infrastructure (PKI) to encrypt the key file. This method uses Public Keys and certificates to encrypt the key file. You would encrypt a new copy of the key file for each user that would need to access the key to decrypt the password. To use thismethodyou would use objects inthe .NET Framework and trusted certificates. This is a more complicated solution requiring a built out PKI infrastructure. I may present that method in another blog post.
I would encourage you to use the AllSigned PowerShell script execution policy and digitally sign all your scripts. While this requires additional configuration and obtaining a Class 3 Microsoft Authenticode Code Signing certificate, it can add an important additional security mechanism. Your password security does not matter much if someone can just alter your scripts that are configured to decrypt the password and run. They could put in whatever code they wanted to do what they wanted and when your script executes as a job that you have alreadyschedule, then they have accomplished what they want. If you sign your scripts, PowerShell validates the signature. Validating digital signatures verifies that the file has not changed or was not signed by a trusted publisher. If the file has been changed and not resigned then PowerShell will find the script invalid and will not run it. Denial-of-service attack? Sure, but it is a lot better than running whatever code the attacker wants.
I hope you have found this series of blog posts useful and interesting.