Handling Passwords in Functions

It happens. Sometimes you need a plain text password for something in a function such as requesting an Oauth 2.0 token from a web resource or for passing to legacy things that won’t accept Windows authentication or a [System.Management.Automation.PSCredential] object directly; However, the worst thing you can do is add a [string]$Password parameter to your function and call it a day.

The Wrong Way

Let me show you why you should avoid creating a password parameter with a basic function.

Now, this function doesn’t actually use the -Username and -Password parameters but that’s not what’s important. Take a look at what happens when we call the function from the command line.

The username and password the user passes to the function is there for everyone to see in the command-line history. You might be thinking that’s not so bad because Get-History only shows command history from the current session and closing the window will get rid of it; However, many people use the PowerShell transcript cmdlets in their profiles (sometimes enforced by Group Policy) and others may be using the history options in PSReadline both of which create a physical record of the command. That’s also not accounting for people who leave PowerShell windows open for days on end (myself included.)

The Correct Way

It is possible with a PSCredential object to extract the plain text password inside the function. Using this method you can use a normal -Credential parameter workflow and still pass a plain text password where it’s needed.

The PSCredential Object

PSCredential is an object class in PowerShell that provides a standard way for dealing with usernames, passwords, and credentials. When a cmdlet in PowerShell asks for a  -Credential parameter it’s almost always a PSCredential object in the background. For example:

We can see using the help information for Stop-Computer that  -Credential is indeed a PSCredential parameter and if we call it with (Get-Credential) as the value it would produce the prompt seen above.

NOTE: When calling a parameter at the command-line you can put another command in brackets () to give it’s result as the value. For example Write-Host (Get-Date) . You will also sometimes see people do it as two separate statements though that is unnecessary.

Extracting the Password

Before we play around with the values we’ll need a PSCredential object which is easily created.

Once created if you call the variable you will see the username but notice the password is a System.Security.SecureString.

Calling the Password property on its own isn’t any different.

We need a plain text password we can pass to our end point though so what can we do? How about ConvertFrom-SecureString ?

Nope… ConvertFrom-SecureString provides the secure string as a string so you can save it to a file. It does not actually give the value.

You could use [System.Runtime.InteropServices.Marshal]

but there’s a much easier way, the PSCredential class has a method on it called  GetNetworkCredential() which on its own returns only the username and domain but can also be used to get the password.

Putting it Together

Knowing we can extract the plain text password from the PSCredential object it’s pretty easy to put it in to practice.

* The default value [System.Management.Automation.PSCredential]::Empty  makes sure we have something to pass to other cmdlets or functions expecting a PSCredential object. It’s not relevant to this example but it’s best practice.

Testing it our we see it works as expected:

This way we aren’t exposing passwords to the history, we’re using the standard -Credential parameter workflow, and we still have access to the plain text password value as needed.

SecureString Parameters

Sometimes you may want to get only a password and not a username in which case you could use a [SecureString]$Password parameter. The only catch is that it doesn’t work particularly well if the parameter isn’t mandatory.

If we call the function with the parameter set as mandatory it will pop up a prompt for the password.

You’ll run in to problems if the parameter is optional or the user attempts to call it directly though.

The only way to make it work on one line is to expose the password to the history. To keep the password out of the history or transcripts the user will need to create the secure string as a variable before calling your function.

Leave a Reply

Your email address will not be published. Required fields are marked *