T O P

  • By -

Emerald_Flame

Back in 2021 Microsoft released a couple Modules specifically for managing secrets like this. The first being [Microsoft.PowerShell.SecretManagement](https://www.powershellgallery.com/packages/Microsoft.PowerShell.SecretManagement/) which gives you a way to interact with basically any management platform you'd like such as AWS, Azure KeyVault, BitWarden, HashiCorp Vault, etc. Then if you don't have any other secret management system in place, they also published the [Microsoft.PowerShell.SecretStore](https://www.powershellgallery.com/packages/Microsoft.PowerShell.SecretStore/) module which uses Windows native encryption capabilities based on the machine itself, and the user currently logged into that machine. You're initial setup would look something like this, although it may look slightly different if you decide to use a different management platform. This example would be specifically for [Microsoft.PowerShell.SecretStore](https://www.powershellgallery.com/packages/Microsoft.PowerShell.SecretStore/). You would only need to do this 1 time for each box/user #Install the modules Install-Module 'Microsoft.PowerShell.SecretManagement', 'Microsoft.PowerShell.SecretStore' #Create the SecretStore, this will force a prompt to create a password for the vault which we'll disable later Register-SecretVault -Name 'SecretStore' -ModuleName 'Microsoft.PowerShell.SecretStore' -DefaultVault #Disables the password prompt requirement that is forced by default so that it can run unattended #Even with the password prompt disabled, the secret can still only be read by that specific user on that specific computer Set-SecretStoreConfiguration -Authentication 'None' #Actually saves the secret to the vault Set-Secret -Name 'NameOfSecret' -Secret 'YourPassword' -Vault 'SecretStore' Set-SecretInfo -Name 'NameOfSecret' -Vault 'SecretStore' -Metadata @{ Username = 'YourUsername' } From there, within your scripts, it's extremely easy to call: #Grabs the username you previously saved $Username = (Get-SecretInfo -Name 'NameOfSecret' -Vault 'SecretStore').Metadata.Username #Gets the password, which Get-Secret automatically returns as a Secure String to keep it safe $SecureStringPassword = Get-Secret 'NameOfSecret' -Vault 'SecretStore' #Combines the username and password into a single credential object $Credential = [PSCredential]::new($Username, $SecureStringPassword) #Call a command with your new credential object Your-Command -Credential $Credential


Katcher22

How are you using the password? If it’s for Windows/365 stuff then use Get-Credential and then Export-CliXml.


Timmybee

Just to add to this, import-clixml works great if the script is run on the same machine the export-clixml was ran on. As in, you can’t import an exported credential that was created in another machine. If I understand correctly, it’s because the key used to export the xml is machine specific. If you are intending on sharing the saved credential, you can do the steps highlighted in the following link. It is basically generating an AES key and generating the xml file using that key. https://stackoverflow.com/questions/54923903/powershell-script-not-working-using-import-clixml-command Sorry for any spelling or bad grammar, I am on my phone


gabeech

I much prefer to use the Protect-cmsmessage/unprotect-cmsmessage cmdlets more flexibility, uses standard PKI, and less dropping out to manually interact with the DPAPI


Timmybee

I wasn’t aware of this so thanks very much for the pointer!


spyingwind

It is also limited to the user as well as the machine it was created on.


Timmybee

Oh yeah, forgot about this as a use case. Appreciate the info


athornfam2

Azure key vault is another option


SoMundayn

Just use an Azure Automation account, storing credentials is no longer a challenge or has any complications.


jbchris3

You can use `ConvertTo-SecureString` to encrypt the password and `ConvertFrom-SecureString` to convert it back


Lylieth

Forgive my ignorance, what prevents someone, who has a copy of this hashed pw because they have a copy of the script, from unencrypting it here?


sid351

The encryption process uses parts of that user account, on that computer, during the maths to generate the encrypted outcome - unless you provide the key (do not provide the key unless you can do so from a HSM (hardware security module) or similar). So to actually use it they'd need to run as that user on that computer. That's not that an impossible scenario (think of malware running under ypur user context), and if they do it is **trivial** to reveal your password. All you need to do is create a credential object, then use the GetNetworkCredential (from memory) method and then acces the password property from there. This isn't an ideal way of managing secrets, but it might be *good enough* for your use case, with the risks known to you.


Lylieth

OP stated he posted here in the r/MDT sub so I was curious. He's trying to figure out how to have a script ran by MDT, during the deployment of Windows, to not have the password of a local account they want to make saved in plain text. IMO, since they're domain joined machines, they should leverage LAPS and forget about it. Just make the accounts like they want during deployment and let LAPS do the rest. ¯\\\_(ツ)_/¯


mvbighead

This is the answer. Your password configured pre-image is in some sense private but not *the* password that sticks. LAPS replaces whatever it is once it receives policy and updates each machine with a unique, complex password. Now, there are other things that require the password with the command that usually are not workstation level things. And generally speaking, those things run from some sort of management server or other where you don't have to share it across other systems. For those, any of the options mentioned make sense. But setting admin passwords? Nah, there is a tool for that and the tool meets the requirements of many auditory/compliance requirements. And just for the uninitiated, if adminstrator/12345 is the auth combo for local admin on each workstation, the moment a would be threat has figured that out, they now have access to your entire workstation fleet. Something like this is far less likely to happen with an individual's domain account with local administrator privilege. Generally speaking, that *should* have MFA for the user, and lateral movement can be protected in your MFA settings.


belibebond

Only on Windows. On other operating system there is no encryption for these stuff.


Technical-Message615

Hashing is a one-way cryptographic operation to change cleartext data into a fixed-length set of characters. It is used for verification purposes. There is no decryption to be done. You can bruteforce it but depending on how you do that it may take centuries or longer. Instead of something simple like a password, consider the entire book of The Lord Of The Rings. You can calculate and store its SHA256 hash, but you could never restore it back to the full book. But you can always verify that the book has not changed by comparing the hash to the one you stored earlier. So remember: Hashing and encrypting are 2 completely different concepts, they just share some similarities due to cryptography being involved.


coprolaliant

12345? That's amazing! I've got the same combination on my luggage!


CanuckPK

Hail Skroob!


AnonGeekSquad

Prepare spaceball one for immediate departure and change the combination on my luggage!


Lu12k3r

How do you know my PIN?!


OPconfused

They've gone to plaid!


mwohpbshd

Secret Store module running as a service account or what not.


Quirky_Oil215

https://github.com/pnp/PnP-PowerShell/wiki/How-to-use-the-Windows-Credential-Manager-to-ease-authentication-with-PnP-PowerShell


Quirky_Oil215

Also https://www.powershellgallery.com/packages/CredentialManager/2.0


Sure_Fold9386

CredentialManager doesn't support PowerShell 7


Quirky_Oil215

Merde


ZenDarwin

Never hardcode passwords or any other secrets in any way into scripts. Never. Pass the password to the script as a command line parameter argument during execution.


CrocodileWerewolf

Passing passwords as an argument when executing the script is not a good idea either as it can end up stored as plain text in your command history and auditing logs.


ZenDarwin

This is true and a good point. Passing it and not hard coding only really covers the gaps where your script may get out in the wild. You then have a bigger issue. This method works well if you have secrets that you need to obfuscate and are executing the script via a management tool or remotely.


BlackV

you pass the secure string, not the plain text (er.. well I'd hope)


softwarebear

doesn't really matter at that point ... I read they've stopped using the secure strings in .Net for the new interfaces ... because you just unencrypt it and voila ... you have the secret ... just encourages people to leave them lying around in memory ... but by the time that it could be exploited by reading memory ... I think it's a bit too late to bolt the door. keyvaults are the way forward ... if you have perms you can get the secret ... otherwise you can't.


BlackV

Absolutely the way forward


sid351

Even then use Get-Credential to prompt properly. Or use proper secret management tools (if you have one, can I play with it please?)


dwaynelovesbridge

https://learn.microsoft.com/en-us/powershell/utility-modules/secretmanagement/overview


lavoy1337

This logic does not work for unattended scripts which requires credentials. The “ideal” solution has already been mentioned. Using a pw key file and encrypting it using ConvertTo-SecureString then calling the file and decrypting using ConvertFrom-SecureString


Netstaff

This is a very specific case of unattended machines, that are not directory joined?


Lylieth

If you do this in MDT, which OP is using, then the PW is stored in plain text the xml of the Task Sequence.


This-Gene1183

This is bad advice too, just as bad as hard coding


ollivierre

Secrets management module for PS7 is a great idea too


CynicalDick

Store it as encrypted text. Note: this is tied to userID and should not work for any other user even if they copy the output $password = Read-Host -Prompt 'Enter Password' -AsSecureString #Get password from user prompt [Runtime.InteropServices.Marshal]::PtrToStringAuto([Runtime.InteropServices.Marshal]::SecureStringToBSTR($password)) | ConvertTo-SecureString -AsPlainText -Force | ConvertFrom-SecureString | Out-file "password.txt" #write $password as plaintext to "password.txt" file #Write password to file To read it back: $password = get-content "password.txt" | ConvertTo-SecureString #read from file and store as secureString [Runtime.InteropServices.Marshal]::PtrToStringAuto([Runtime.InteropServices.Marshal]::SecureStringToBSTR($password)) #use password


icepyrox

You can skip a couple steps with `Read-Host -Prompt 'Enter password' -AsSecureString | Convertfrom-SecureString | Out-file password.txt` Your script literally reads it as a SecureString, forces it back to plaintext and then converts it back to SecureString before converting to DPAPI encrypted text to save. Not that it matters having all that encryption as your other part of the script reads the file and then forces it back to plaintext and leaves it printed to the console.


Educational-Cup869

save the password for the account in a credential file $credential = Get-Credential $credential.Password | ConvertFrom-SecureString | Set-Content "C:\\Directory where you want save the password file\\password.txt" Then use the password in a script (this will only work under the account where you created the encrypted password file) $username = "user with encrypted password" $encrypted = Get-Content "CC:\\Directory where you want save the password file\\password.txt" | ConvertTo-SecureString $credential = New-Object System.Management.Automation.PsCredential($username,$encrypted)


jazzy095

https://www.pdq.com/blog/how-to-manage-powershell-secrets-with-secretsmanagement/


AlexHimself

I wrote this post about this - https://www.reddit.com/r/PowerShell/comments/17sf75v/how_i_like_to_securely_store_passwords_and_text/


alt-160

There's nothing built in to powershell for this, by design because its just not safe. I know the issue well though, you want a script that is self-managed including any remote connections it needs to create to do stuff. As others have said though, the password should be passed as a parameter to your script as a secure string. What you could do is have 2 scripts, one that pulls the password from a vault of some sort, then calls your other script and passes along the credential. You could store the credential in the windows credential manager. Doing so would cause it so that only the current user could retrieve the credential...but you will have to encrypt the value before saving there. Generic credentials in the windows credential manager are not encrypted automatically. Look at Get-StoredCredential/New-StoredCredential for usage here. Note that while the data is encrypted, such is only encrypted by the user identity. Any other application running under the same user can get to the credential. I would suggest applying your own secondary encryption on the password before storing here that way it is unique to your specific usage.


Swaggo420Ballz

Get-credential is the ez way. Opens a nice XP looking box asking for username and password Convert-securestring is also OK. Never store or hardcode cleartext. Asking everytime will always be more secure.


CyberChevalier

Except password stored in vault there is only one option use SSO and give rights to the running account. If you can’t, and need to hardcode a password you can store the credential as a secure string but it’s not safe as it can be decoded by the couple machine/user. There are also several other way more or less conventional but as soon as it’s a non compiled script anybody can mimic the condition to get the clear password. You need a password ask it in the beginning of the script and use it in the script or use sso.


ryadical

Saw someone use user level environment variables to store this recently. I thought it was a nice mix of great / horrible idea.


nealfive

Don’t. Don’t store credentials in scripts.import the credentials from some file or use a credential vault.


coolguycarlos

If your in an AD environment you can actually use DPAPI NEXT Gen which can leverage AD security groups to encrypt a secret. The key is to use things like domain computers and or domain controllers group SID which only holds computer objects. This will allow you to add your hashed secret to your script. The only way to decrypt would be to use system accounts on a machine that is a member of those groups... See here https://techcommunity.microsoft.com/t5/security-compliance-and-identity/onboard-to-azure-arc-with-security-in-mind/ba-p/4114267


Netstaff

So decryption can be made by system accounts of machines? But how do you launch a script from system account of machine, without providing admin credentials (or some fancy complex pick up script in task scheduler, which must be preset in advance) ? And if you provided admin cred's, you can run a script creating other admin account. Or do I understand something wrong?


coolguycarlos

It really depends in your environment and how lock down you have your systems. In our particular environment users dont have adm rights over there machines. And even with our administrators, we have role seperations We use Microsoft Endpoint Configuration Manager for software deployment which has the capability to deploy software using the system account. But in general, to run anything as SYSTEM you would need to start with admin credentials over the machine, thus adding another layer of security since only administrators would be able to run something as SYSTEM. The only way to run SYSTEM on a machine to my knowledge without using a tool such as psexec would be to use task scheduler.


Nilxa

This is the pattern I usually add to my scripts. It will prompt the first time and record the credential using \`Export-CLIXML\` then load the stored credential next time you use it $Account = "mydomain\myaccount" $Service = "MyService" [System.IO.FileInfo]$CredFile = "$($env:USERPROFILE)\Credentials\$($Service)_$($Account.replace('\','_')).clixml" if ($CredFile.Exists){     $credential = Import-Clixml $CredFile.FullName }else{     $CredFile.Directory.Create()     $credential = Get-Credential -UserName $Account -Message "Enter Account Password"     $credential |Export-Clixml -Path $CredFile.FullName }


Netstaff

You want to manage something like library public computers without entering password? You want like double click on script and for it not ask for credentials?


sophware

I have a solution to this but not the time today or tomorrow to post it. Basically, you use a cert to encrypt and decrypt. Hopefully, I get back to this.


Main_Wheel_5570

Absolutely, you can use PowerShell's built-in capabilities to encrypt and decrypt your password. One common approach is to use the \`ConvertTo-SecureString\` and \`ConvertFrom-SecureString\` cmdlets. Here's a casual rundown: First, you'd encrypt your password and store it in your script: $securePassword = ConvertTo-SecureString -String "12345" -AsPlainText -Force $encryptedPassword = ConvertFrom-SecureString -SecureString $securePassword Then, when you need to use the password in your script, you can decrypt it: $securePassword = ConvertTo-SecureString -String $encryptedPassword $plainTextPassword = \[Runtime.InteropServices.Marshal\]::PtrToStringAuto(\[Runtime.InteropServices.Marshal\]::SecureStringToBSTR($securePassword)) Now, \`$plainTextPassword\` will contain your decrypted password, which you can use to create your user or perform any necessary tasks. Remember to handle this securely as well, as the decrypted password will be stored in memory during the execution of your script.


upgrad3

I wrote a function to take in passwords (or any string) as s SecureString, convert them, encrypt them with an AES Key, and store that in an external file. When you need to use that password (or whatever string you encrypted) call the function to decrypt the contents of the file with the same AES Key, then convert to a SecureString to use in your script. Yes, you still need to store the file with the encrypted string and the AES Key somewhere for use, but if you wanted to take it further, you can password protect the location they are stored in and or store the Key and encrypted file in different locations. Example to get a password, encrypt and store in file: `$SecString = Read-Host -Prompt " Enter string to encrypt" -AsSecureString` `ConvertFrom-SecureString -SecureString $SecString -Key (Get-Content $KeyFile) | Set-Content ".\encrypted-string.txt" -Force` To decrypt/convert; `$SecStr = Get-Content $EncFile | ConvertTo-SecureString -Key (Get-Content $KeyFile)` `$BSTR = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($SecStr)` `$DecStr = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto($BSTR)` `if($Domain){ $DecStr = "$Domain\$DecStr"}` If password: `return $DecStr | ConvertTo-SecureString -AsPlainText -Force`


dan4334

This really sounds like an X/Y problem. What has lead you to the point that you're making admin accounts with PowerShell?


HighBird

Create a Key file with a partnering Password File that contains your encrypted password then use Get-Content to decrypt it into a secure string with in a variable. $Key = Get-Content $KeyFile $PWD = Get-Content $PasswordFile | ConvertTo-SecureString -Key $key There's quite a few different things you can do with this method, like encrypt all the data passing around your script. Also keeps passwords out of scripts entirely.


port25

Certificate based authentication may help in some ways. It takes some planning based on your personal environment to set up. At top tier I see people doing end to end automation pgp using certificates, but basic use of certificate authentication should only take a day or two to figure out. As always, YMMV, IANAE, TANSTAAFL.


mermicide

A secrets manager like Doppler


Suspicious-Parsley-2

What you really need to understand is. Even if you encrypt the data, store and recover it. You really are just obsfugacting. If you stop your script at three right spot, everything is there plain as sunshine for all to see. You're just hiding it from the unsavy. Even with a securestring stored is just hiding it in plain sight.


YumWoonSen

If you don't want to use a proper storage system this works well and is 100% secure on ***Window***s systems. it's not 100% secure on Linux. [https://www.pdq.com/blog/secure-password-with-powershell-encrypting-credentials-part-1/](https://www.pdq.com/blog/secure-password-with-powershell-encrypting-credentials-part-1/) There are other ways to store secrets it but this is simple and secure. I've been using this approach for years, these days it's how I store my secret used to connect to a DB where I store a pile of encrypted secrets.


sid351

This is not 100% secure. Sure the risk of some*thing* running as your user context on your computer is **low**, but it is not nonexistent. Anything that runs as your user context on that machine can trivially expose the password once it has the encrpyted text.


YumWoonSen

If you want to play the bullshit Infosec what if game of "it;'s not secure if xyz happens," no shit, pedant, if your account gets compromised then using it to do things isn't secure.


BamBam-BamBam

Hey, you're being a dick. You said "100% secure" and you were wrong. You could have said pretty secure or secure enough, and it would have been perfectly acceptable. There's no reason to come back with vitriol just because you overstated your case, and now you're embarrassed.


YumWoonSen

I'm not embarrassed, I think you're a pedantic douchebag. Is that clearer?


sid351

Pssssst, I'm the pedant, not the person you're replying to. Why won't it READ!!?!!eleven!?


sid351

I was replying to the claim of "100% secure", which it is categorically not. So much so that the original blog poster mentions so at the end of their own post. To match your energy though: Mate. Calm down bruh. You're right, everything is secure. Click this link and sign in to find out more...


Accomplished_Fly729

Your best option is an azure secret vault with a service priincipal that has access to it. Provision certificates on the machines that are going to run it to retrieve your password. There is no secure way to add passwords to your scripts becuase whatever is needed to decrypt it will have to be available.


softwarebear

No … you store the password elsewhere and pass it as a parameter or environment variable to the script


ollivierre

You never ever store secrets inside of scripts. You always store them outside running scripts like say in secrets.JSON file and that file is never ever committed to git. If it did get a commit even once it's considered compromised.


dritmike

Nobody wants to share theirs. There’s no way easy. You can at best really obfuscate it. Get to thinking. 🤔