Recently, I was browsing through some old code and stumbled upon a snippet I had used for testing. The code itself wasn’t particularly interesting, but what caught my attention was the presence of a username and password in plain text. This is, of course, far from ideal. In this case, it was a test account that had long been deleted, but it could just as easily have been something valuable to a malicious actor. In this post, we’ll explore a way to make this process safer.

Why Do We Add Credentials to Code?

If you have a script that only runs when someone manually triggers it, you can simply prompt for credentials at the start. However, many scripts run automatically, for example via a scheduled task or a cron job. In those cases, credentials need to be stored somewhere, either in the code or in a separate configuration file. It’s critical to ensure that this file is not publicly accessible.

Another scenario is when you build an application used by multiple people. For example, an app that performs a task under a different user or service account, where the user should only have the permissions required for that task and nothing more.

An Example of What Not to Do


# Bad example - credentials in plain text
$User = "johndoe@thebigfirm.com"
$Password = "S4perS3cr3t!!"
$Credential = New-Object System.Management.Automation.PsCredential($User, $($Password | ConvertTo-SecureString -AsPlainText -Force))

Anyone who opens this file can easily read the credentials and impersonate “John Doe.”

A Safer Approach

We’ll solve this by encrypting the password. This looks similar to what we did above, but now we’ll expand the process and stop storing data in plain text. First, run the following code under the user who needs to execute the script. This can be done while you’re logged in as that user or via a scheduled task that runs under that account.


# Step 1 - password from plain text to encrypted value
$Password = "S4perS3cr3t!!"
$EncryptedPassword = $Password | ConvertTo-SecureString -AsPlainText -Force | ConvertFrom-SecureString

## Output: 01000000d08c9ddf0115d1118c7a00c04fc297eb010000007f5ecd7ab37e9e438e4b2fe7ee769680000000000200000000001066000000010000200000006b14af4fb9d38553f9e594fb80d558b1222e0d763d62af613cba403f4f7c7d69000000000e80000000020000200000002bd0429f12063726618cc209c58814ccee5188a1497363c308735f4ce41664a7200000007b9d537b4ac1e59be624d8fd401566aa5fd13959a206000ffdc16fb833f5f2f94000000066b89c64700e2298e0d62031331b6a89e85cc8567e8572f5c26cb5580afbcbc01ad543db1f33e612a1a8ddc9d3f21f4640272c5ec467e12c2cc41d1ffda84d2b

Retrieve the value of $EncryptedPassword. If you’re logged in, you can simply query it; otherwise, write it to a temporary file.

What’s Happening Here?

We convert $Password into a secure string. This is an object, which makes it hard to store in a script file. That’s why we convert it back into a readable text format using ConvertFrom-SecureString. In PowerShell 7+, you can omit -Force since it’s obsolete, but it still works for backward compatibility.

Isn’t this still “kind of” plain text? It looks like it, but it’s completely unreadable unless you’re on the same account and machine. By default, ConvertTo-SecureString encrypts the string using a combination of account and machine. So this string will differ if you run it under another account on the same machine or under the same account on a different machine. This already makes it much safer than plain text. You can also use the -Key parameter to add your own key. But of course you need to store that also safely.

Using the Encrypted Password

We can’t use this encrypted string as-is. To use it as a password, we need to convert it back into a Secure String:


# Step 2 - password from encrypted value to Secure String
$EncryptedPassword = "01000000d08c9ddf0115d1118c7a00c04fc297eb010000007f5ecd7ab37e9e438e4b2fe7ee769680000000000200000000001066000000010000200000006b14af4fb9d38553f9e594fb80d558b1222e0d763d62af613cba403f4f7c7d69000000000e80000000020000200000002bd0429f12063726618cc209c58814ccee5188a1497363c308735f4ce41664a7200000007b9d537b4ac1e59be624d8fd401566aa5fd13959a206000ffdc16fb833f5f2f94000000066b89c64700e2298e0d62031331b6a89e85cc8567e8572f5c26cb5580afbcbc01ad543db1f33e612a1a8ddc9d3f21f4640272c5ec467e12c2cc41d1ffda84d2b"

$User = "johndoe@thebigfirm.com"
$Credential = New-Object System.Management.Automation.PsCredential($User, $($EncryptedPassword | ConvertTo-SecureString))

Now we have a password in our code that cannot be traced back to its original value unless you have access to the source user and machine. If multiple users need to use these credentials under their own accounts, you’ll need to create an encrypted password string for each user. In that case, consider storing these strings in a file and retrieving the correct value per user. For example, in a file located in the user’s Documents folder.

Leave a Reply

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