PowerShell Credentials and SecureStrings, Part I
I frequently get asked questions in my PowerShell classes about providing credentials to commands and scripts, including storing passwords in scripts. In this blogpostI’ll discuss howthe .NET Framework handles strings and why that behavior is undesirable for working with passwords and other secure strings of data. I will also discuss methods of providing passwords, credentials, and other secure strings into our commands and scripts interactively. AttheendI will introduce a method of securely saving credentials so that a script can be executed without prompting for passwords, although the script must be executed by the user who entered the credentials. In afollow uppost, I’ll discuss options when you need multiple people to execute scripts with saved credentials.
With VBScript, or any other Windows Scripting Host language, the most common method of saving credentials was to save the username and password in clear text in the script file. The danger of this should be obvious: any passwords, usually associated with accounts with elevated or administrator permissions, can easily be read by anyone (hackers included). Some administrators used Windows Encrypting File System (EFS) to encrypt the file. That can work well, and access to the file can even be shared to individuals with private key recovery, if the Active Directory environment was configured correctly (Certificate Services + Group Policy Auto-enrollment + Group Policy Credential Roaming + Private Key Archival). EFS is certainly a viable option for PowerShell, and if the environment is setup, then it’s an excellent option. If not, PowerShell has additional options.
PowerShell and .NET Framework String and SecureStrings
Strings in the .NET Framework (and PowerShell is the .NET Framework scripting language) have two important properties that relate to password security: One, strings are immutable; Two, strings cannot be disposed of by the “user”. Immutable means that the string, once created, is fixed. PowerShell syntax makes it seem like the string has changed. The .NET Framework creates a new string object with the “modified” value and updates your variable reference to the new object. The old string object that, say, contains your password in clear text, remains in memory untilthe .NET garbage collector cleans it up (disposes of it). The second point means that you cannot dispose of the string yourself if you wanted to. With your string in memory, any memory dump, whether accidental or malicious will contain that password in clear text.
To help in these situations, in the 2.0 version ofthe .NET Framework, Microsoft introduced the SecureString class. SecureStrings have several nice advantages for use with passwords:
- They are NOT immutable,thereforecan be changed in memory.
- They implement a Dispose method, so you can manually dispose of them inscript.
- Do not store the string in clear text in memory, so a memory dump will not gain someone anything useful.
Many PowerShell cmdlets have a –PSCredential parameter that accepts a PowerShell credential object. The primary method of building a credential in PowerShell is to use the Get-Credential cmdlet. Get-Credential creates a graphical prompt to the user asking for the credentials. The object returned by this cmdlet contains a Password property that itself returns a SecureString. The password supplied by the user is never stored in clear text in memory. This object can then be passed to any cmdlet that has such a –PSCredential parameter. However, not every cmdlet that allows alternate credentials accepts a PSCredential object. Those cmdlets usually build the credential for us from two separate parameters, ausernameand a password. Those passwords need to be supplied as a SecureString object. This is true even if you are willing to store your passwords in clear text in your script files (and hopefully use something like EFS to encrypt the files).
Providing a SecureString to PowerShell
PowerShell contains several cmdlets that let you create or manipulate SecureString objects. The easiest way to create a SecureString object is to use the Read-Host cmdlet with the optional –AsSecureString cmdlet. Using this parameter directs PowerShell to obfuscate the string typed into the prompt and return the string as a SecureString. You can now use this object with any cmdlet that needs a SecureString object. (Seescreen shotfor example:
That cmdlet is great and works in many situations, but not in any situation where you need to run a script automatically through some kind ofscheduler, like Windows Scheduled Tasks or SQL Server SQL Agent Service jobs.
Creating a SecureString from Plaintext
If you want to save the password in the script in Plaintext so that the script does not prompt when it’s executed, then you can use the ConvertTo-SecureString with the optional –AsPlainText parameter. Doing so will take a String object, make a copy as a SecureString. This will leave a String object with your password in memory, making the system vulnerable to a memory dump attack. This technique should only be used on secure, trusted systems with a high level physical and network intrusion security in place. Even if you encrypt the file, there is still the risk of memory access to the password when the script runs. To ensure that administrators are aware of the risk, Microsoft requires an additional parameter when the –AsPlainText parameter is used: –Force.
Creating a SecureString from an Encrypted String
What is the recommended method of storing passwords and using them in PowerShell? Well, Microsoft would like us to store our SecureString passwords as encrypted strings into files, and then decrypt them back into SecureStrings when we need them. To do this, use theConvertFrom-SecureString cmdlet on the output of the Read-Host “Enter Password” –AsSecureString command. If you use the ConvertFrom-SecureString with no parameters, then PowerShell uses the Windows Data Protection API (DPAPI) to encrypt the password, then you can save it to a file. Using the DPAPI uses a user encryption key. Unfortunately, the way that PowerShell uses the DPAPI means that only that user on that computer can decrypt the password in the file. Make sure you are logged on as the user you plan to run any automated job as when you encrypt the password. Use the Set-Content cmdlet to write the encrypted password string to file.
To rebuild the SecureString for use with cmdlets that need the SecureString, use the ConvertTo-SecureString cmdlet on the content read from the password file. Again, this only works if the PowerShell is run using the user account that was used to encrypt the password.
For cmdlets that need a whole credential object, it’s only one more command, using the New-Object cmdlet to create a new credential object.
What about encrypting the password so that multiple users can access it to execute scripts? I’ll save that situation and other security considerations for later blog posts. Hopefully you have found this useful for working with passwords and SecureString objects. The same techniques can be used for any string you need to secure, not just passwords.