You already know that using credentials in your code is very much frowned upon. Every security expert will tell you that there are now plenty of other ways to verify access beyond user IDs and passwords, and you should always use the best verification method. But what if you need to connect to some legacy database, spreadsheet, server, or some other older piece of technology? You may not have any choice other than to use a user ID and password combination. If you solely live in a PowerShell world, you likely use stored credential objects; but what if your connection happens to be outside the realm of PowerShell?
Why is putting credentials in your code a problem?
If you hard-code user IDs and passwords in your code, you practically advertise them to all users on that computer. With Script Block Logging enabled (as it should be), your users just have to look at the code in your Windows Event log to see the user ID and password for that network database. This is very much like leaving your valuables visible in your car at the mall—you should at least put them in the trunk to prevent crimes of opportunity.
I am aware that many computer security folks now scream, “There is no security in obscurity”, while commencing with some agitated hand waving. Nonetheless, you put your shopping bags in the trunk and you lock your car at the mall. Sure, it does not deter the professional car thief or the repo man with a flatbed truck; but it is still a common-sense practice and is recommended repeatedly by every local police department.
So from now on, add your credentials to a packaged script—where they are saved encrypted—and load them into a variable only at runtime.
If you now ask, “How are they encrypted?” and “With what algorithm to which standards?” you are missing the point. This mechanism does not provide any real security from a person determined to get these credentials. Once you run the packaged app and attach with a debugger, you can always find a way to see the credentials. Adding credentials to a packaged script does not get around that; having some stranger sit at your computer and debug your application is an entirely different problem. So this new mechanism we are introducing prevents your credentials from being advertised in Windows logs for everyone to see. Your code changes from this:
Connect-Database -User “DarthVader” -Password “LukeSkywalker1977”
To this:
Connect-Database -User $SAPIENHost.GetUserID(0) -Password $SAPIENHost.GetPasswordString(0)
You can also use $SAPIENHost.GetPassword(index) to obtain a SecureString object containing your password.
The index used in these calls is based on the order in which you specified credentials in the Script Packager tool. Please keep in mind that indexing starts at 0, not 1.
You should definitely check that $SAPIENHost is not null before making these calls. Running a script using these methods in a default PowerShell console will only produce errors.
Requirements
This mechanism is only available in packaged scripts, and when running your script from PrimalScript or PowerShell Studio—you will need PrimalScript 8.0.159 or PowerShell Studio 5.8.199 at a minimum. The packager engines supporting this are for Windows PowerShell 5.1 and PowerShell 7 versions 7.0.8, 7.1.5, and 7.2.1 or later.
As always, if you have any feedback or suggestions, let us know in the comments or in our support forums.
Here’s a simple function to retrieve the password for a given username without having to know the index number:
function Get-SapienPW
{
param ($userName)
# first, see which indexes have data
$MaxIndex = 0
while ($SAPIENHost.GetPasswordString($MaxIndex))
{ $MaxIndex++ }
$MaxIndex–
# next, find the index containing the username
$index = 0
while ($index -le $MaxIndex)
{
if ($SAPIENHost.GetUserID($index) -eq $username.trim()) { break }
$index++
}
return $SAPIENHost.GetPasswordString($index)
}
Is it possible to use this for API credentials?
Technically it can be used for anything that is an identifier / key type of pair. Even if you only want to store a key and throw away the user id so to speak, you can use this.
Does this work with Windows Services?
The Service host currently has no SAPIENHost object, so no.
Feel free to file a feature request here:
https://www.sapien.com/requests
This feature is a game changer! No longer do I have to pull credentials from credential files, or Enterprise Vault. Thank you so much for this feature!
Glad to hear you like it. The next builds of PowerShell Studio and PrimalScript will also add this feature to scripts packaged as a service.
I’ve been unsuccessful in my attempts. Anybody able to share a basic example?
Please post in our support forum. We can help you there.
Ok, so I must have missed this:
This mechanism is only available in packaged scripts, and when running your script from PrimalScript or PowerShell Studio
This was after I packaged my script into an executable and it stopped working when running the exe. So, this feature only works when running the code within PS Studio? That’s a bummer, I was under the impression (originally), that this would work for packaged executables ran from other servers/computers. I like to publish my packaged scripts as remote applications on an RDS server. I guess I’ll have to continue using credential files or pulling creds from a password vault then.
Language sometime is ambiguous. It will work for packaged executables, provided you have added the credentials to the resources,
Just saw that my feature request to add this capability to the Windows Service Host was approved and available in version 5.8.203
Can’t wait to test this out.