Saving secure string in persistent environment variable - powershell

So I'm wanting to save a secure string variable type for a local user. That way I can run a convertfrom-securestring to make rest api calls in a .ps1 file without the password being accessible. Is what I'm trying possible?
The code I'm using is below, but not yet working:
$PlainPassword = "atestpassword"
$SecurePassword = $PlainPassword | ConvertTo-SecureString -AsPlainText -Force
[Environment]::SetEnvironmentVariable('JiraCreds', $SecurePassword, "User")
Are environment variables only saved as strings without support for other data types?

Yes, environment variables are invariably strings, and a [securestring] instance cannot be used directly, because its string representation is simply its type name (System.Security.SecureString).
However, you can pipe to ConvertFrom-SecureString to get a (still encrypted) string representation:
$PlainPassword = "atestpassword" # Don't actually store this in your script.
$SecurePassword = $PlainPassword | ConvertTo-SecureString -AsPlainText -Force |
ConvertFrom-SecureString
[Environment]::SetEnvironmentVariable('JiraCreds', $SecurePassword, "User")
To later use the environment variable to construct a [pscredential] instance (using the current user's username as an example):
$cred = New-Object pscredential $env:USERNAME, (ConvertTo-SecureString $env:JiraCreds)

Related

Encrypting password or workaround

I am bit of a lazy guy, so I have created a script that opens many applications for me. Works fine as ISE opened with Administrator credentials, also opens apps with admin creds, however some of them need a different credentials.
Is it possible, to make powershell remember typed in password each time I log in and open it? (I know that variables are stored only till ps is opened)
Thing is - I cannot store a visible password in profile/text file or in a script, as this is a jump server used by many people. Is it somehow possible to type a password once, make PS encrypt it and each time I will open PS, it will decrypt it and use? or any workaround possible around this?
edit with code:
It's the only part I would like to change
$currentPW = "some password"
$credentials = New-Object System.Management.Automation.PSCredential ("domain\username",$CurrentPW)
start "c:\application.exe" -credential $credentials
It kinda works but it would require me, to input the password everytime I log in to device, so I could go for option like:
$currentPW = read-host "Provide your password"
$credentials = New-Object System.Management.Automation.PSCredential ("domain\username",$CurrentPW)
start "c:\application.exe" -credential $credentials
but this would require me to input the password each time I log in to system and open PS as it does not remember variables after restart.
So...is it even possible to make this work?^^
You can use ConvertTo-SecureString to encrypt the password using the users account key, then save this secure string to a file to load at a later time.
This assumes you are the only one with access to the logon account (not an account with shared credentials), as anyone who can logon as the account can decrypt the file.
$username = "domain\username"
$passwordFile = "C:\folder\EncryptedPassword.txt"
#if password file exists: populate $securePwd from file contents
If (Test-Path $passwordFile) {
$pwdTxt = Get-Content $passwordFile
$securePwd = $pwdTxt | ConvertTo-SecureString
}
#if no file: prompt for password, create file and populate $securePwd
Else {
$password = Read-Host "Provide your password"
$securePwd = $password | ConvertTo-SecureString -AsPlainText -Force
$securePwd | ConvertFrom-SecureString | Set-Content $passwordFile
}
$credentials = New-Object System.Management.Automation.PSCredential -ArgumentList $username, $securePwd
Start-Process "c:\application.exe" -Credential $credentials
If you have PowerShell 3.0 or newer, you can also combine Get-Credential with Export-CliXml to export a PSCredential object as an XML file. Example:
Get-Credential | Export-CliXml "C:\XML Files\credential.xml"
You can then import the credentials using Import-CliXml. Example:
$credential = Import-CliXml "C:\Xml Files\credential.xml"
Note that the password is encrypted using DPAPI, so you can only import the credentials using Import-CliXml on the same computer using the same user account that was used to export the credentials using Export-CliXml.

How can I silently run a remote PowerShell command without having to hard code my password?

I am trying to run:
Invoke-Command -Computer $computer -ScriptBlock {...}
But I get the error "Access is denied" winrm error and I am hesitant to use the following:
Invoke-Command -Computer $computer -Credential $cred -ScriptBlock {...}
where $cred is:
$username = "John Doe"
$password = "ABCDEF"
$secstr = New-Object -TypeName System.Security.SecureString
$password.ToCharArray() | ForEach-Object {$secstr.AppendChar($_)}
$cred = new-object -typename System.Management.Automation.PSCredential -
argumentlist $username, $secstr
It has to be run remotely and it must be silent. So I can't have the PSCredential pop up window mid script.
Can someone please show me or point me to a document that will lead me to a possible solution?
Thank you in advance.
One thing that you can do is encrypt the password and save it to disk. Then you can read that file, and convert the encrypted password to a secure string and make a credential object from that. Mind you, this must be done with the account that will be used to run the script.
'$uper$secret1' | ConvertTo-SecureString -AsPlainText -Force | ConvertFrom-SecureString | Set-Content .\AccountPass.txt
That will save your password to disk in a text file. If you open the text file it will look something like:
01000000d08c9ddf0115d1118c7a00c04fc297eb01000000b584d55e9c47c942904dd30531d3ad070000000002000000000003660000c0000000100000003060266c3c4333a41e7f0e92176fb3d50000000004800000a000000010000000a2c8bbb2a3666c092004bb5e66fd440320000000636a413a6905789e0f3521cea3d8703405897cd5948da955192bcccd08990ffc1400000068c1
5f8ac088ef0972dfce7d5a20ff3bbcdac4cc
Now, the account that created the file will be the only one that can decrypt it, but that account can then run:
$Password = Get-Content .\AccountPass.txt | ConvertTo-SecureString
$Creds = New-Object System.Management.Automation.PSCredential ("$env:UserDomain\$env:UserName",$Password)
Now you have a credential object, without having to save a password in plaintext. As mentioned, the only account that can decrypt the password in the text file is the one that generated the text file, and the text file will have to be updated whenever the password is changed.

Why is force and AsPlainText required for secure string

I'm currently learning PowerShell through online tutorials.
What is the benefit of/why do examples use -Force and -AsPlainText when creating a secure string?
$password = 'P#ssw0rd' | ConvertTo-SecureString -AsPlainText -Force
There is no benefit of using those arguments, rather they are required to show that you understand that your string is not secure, despite the fact you've now placed it in a SecureString. Because you've passed it as plain text, it's already in memory in an insecure manner.
-AsPlainText shows that you want to pass in it as a plain text parameter. -Force is documented as:
Confirms that you understand the implications of using the AsPlainText parameter and still want to use it.
IIRC, -Force suppresses the confirmation prompt from -AsPlainText
$PW = ConvertTo-SecureString -String 'P#ssw0rd' -AsPlainText -Force

How to save credentials of technical user in PowerShell

We have multiple PowerShell scripts which need the credentials of a technical user.
What is a good way to store these credentials securely/hashed while keeping them available to each user having permission to run the script?
We tried hashing as secure string but this requires each user to hash the credentials, as the secure string is tied to the user's profile.
I was unsuccesful trying to implement something like this:
"run script as administrator -> click yes to dialog -> switch to tech user -> unhash credentials -> rehash as logged in user -> save to file"
(The users do have administrator priviliges)
I could have each user enter the credentials once and then save the user specific hash to a file. But I presume there is a best practice for doing this which I haven't found yet.
$username = "user1#domain.com"
$pwdTxt = Get-Content "C:\temp\Stored_Password.txt"
$securePwd = $pwdTxt | ConvertTo-SecureString
$credObject = New-Object System.Management.Automation.PSCredential -ArgumentList $username, $securePwd
# Now, $credObject is having the credentials stored and you can pass it wherever you want.
## Import Password with AES
$username = "user1#domain.com"
$AESKey = Get-Content $AESKeyFilePath
$pwdTxt = Get-Content $SecurePwdFilePath
$securePwd = $pwdTxt | ConvertTo-SecureString -Key $AESKey
$credObject = New-Object System.Management.Automation.PSCredential -ArgumentList $username, $securePwd
# Now, $credObject is having the credentials stored with AES Key and you can pass it wherever you want.
You can put any key to encrypt the credentials and similarly you can decrypt.
Hope it helps.

Powershell and running scheduled task as other user

I have a dilema.
I am trying to set up a scheduled task in Windows that runs a powershell script as another user (a Service account that has access but no logon rights). The issue is that we have been told by our security group to not code in passwords (obviously good advice) but the connection string for the SQL seems to need it in plain text. I am getting around this by creating a password file:
$credential = Get-Credential
$credential.Password | ConvertFrom-SecureString | Set-Content e:\temp\password.txt
And then in the script converting it back to plain text (to be used in a connection string)
$password = cat E:\temp\password.txt | ConvertTo-SecureString
$BSTR = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($password)
$UnsecurePassword = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto($BSTR)
$connectionString = "Data Source=<mydatabase>;Initial Catalog='<mytable>';User ID=tng ;Password=$Unsecurepassword;"
The snag though, is that when I create the password file and run the script as myself it works great, but I can't seem to run this as a scheduled task. In past experiences I have seen where the password file probably needs created by the service account running the scheduled task, but without local log on rights, I am not sure how to create this. Any thoughts?
I tried this technet article but it appears that it still requires local log on from the other account.
Found the answer - I needed to add a key to the secure string:
When creating the file - adding in the $key:
[byte[]] $key = (1..16)
$credential = Get-Credential
$credential.Password | ConvertFrom-SecureString -key $key | Set-Content e:\temp\password.txt
And then when reading it back in:
$passwordfile = "E:\temp\password.txt"
[byte[]] $key = (1..16)
$securePassword = Get-Content $passwordfile | ConvertTo-SecureString -Key $key
$BSTR = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($securepassword)
$UnsecurePassword = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto($BSTR)
Answer found thanks to this link