Sometimes when writing a Powershell script you need user credentials. Here are a few ways of doing that. We'll be using "johndoe" as the username, and "SuperSecret" as the password.
The Simplest Way - Hardcode them in your command
This is also the ugliest and least secure way. In this hypothetical case, the commands simply expect the username and password as arguments to the command:
Use Variables
Not quite as ugly, but still ugly.
Prompt the User for a PSCredential Object
Now we're getting to a more secure option.
This will pop up a window, prompting the user to enter his username and password. The script can then access the username with:
and the password with:
Assuming our commands take credentials in the format of a PSCredential object, the commands might look like this:
But if they require an actual username/password, you might think that this will work:
But it won't, because the "$Creds.Password" value is in a special format itself, called a "SecureString". You can see this by simply typing the name of the variable:
PS > $Creds
UserName Password
PS > $Creds.UserName
PS > $Creds.Password
But it's easy to convert that SecureString back into plain text:
So now our commands would become:
Ideally our command would be a Powershell command that natively understands the PSCredential format:
Load Up a PSCredential Object From Within the Script
This is another insecure method, but should be known about.
Since, as mentioned before, the Credential object requires a SecureString for the password, we first need to create a SecureString password:
Normally the "ConvertTo-SecureString" routine expects its input to be in the format of an encrypted string of text. It's "native languages" are SecureString and Encrypted String. You can see the Encrypted String format by converting the other way:
which will produce something that looks like this:
00000000e80000000020000200000008ce0cd22aa3fcbb938f08f640201e1bb1c91500b679346a02e48815a385101a2200000006e2055f816b37e010e1938e59938d244c6170307ef651db8e95920f0c06b16d540000000f6b72c9148c628
c579209cd761fd0a2d6ebbdfbae888462733b06f08aec293e02f3a62b83ded1a10d24e370ab5b584bc31bf1816273e8761577b0a7455545881
So to be clear, the "ConvertTo.../...From..." routines convert (natively) between SecureString and Encrypted Strings, not to/from plain text. To include the plain text in the process, we have to tell the "ConvertTo..." routine that its input is "AsPlainText", and to get the plain text back out, we have to use a completely different routine, as we did above, the "GetNetworkCredential()" routine.
Now that we have the password as a SecureString, we can create the PSCredential object:
And now you can use the PSCredential object just as we did above:
Load Up a PSCredential Object From an External File
This is kind of a compromise between the security of prompting the user for the credentials and having the credentials associated with the script itself. The password will be in an Encrypted format, and in a separate file, but it would still be pretty easy for a "hacker" to grab the file and read the password. So it's certainly not secure. You could put the external file on a secure server, but then you'd have to deal with the credentials for logging into the secure server, so all that does is kick the problem down the road a ways. Still, it might be the best we can do without going to extremes.
But the most onerous caveat about this method, at least for general purpose scripts, is that the password encryption can only be decrypted by the same user account on the same computer as was used to do the encryption. You can't write/develop the script that uses this method on one computer, and then run it on another computer, or as a different user.
Some of the following is likely a repeat of what was said above; it's a copy-and-paste from another article I had started/
Reading the Creds From a File
If you don't have a user sitting in front of the computer when the script runs, you can store the username and an encrypted form of the password in a file, beforehand, and then read it from that file when it's needed. Be aware that this is still not secure, but it's more so.
Saving the Credentials to an External File, Beforehand
Not in your script, but just at a Powershell prompt, enter the following:
PS > Set-Content -Path ".\extras.zip" -Value "johndoe"This step creates a file named "extras.zip", over-writing any existing files of that name, in the current directory. The file contains the username. You can verify the contents of this newly-created file with:
PS > type ".\extras.zip"As you can see, it's not really a .zip file; it's just a plain text file. But naming it as a .zip is a minor mindgame, hoping to discourage the casual "hackers" from bothering to try and open / look into the file. It's a weak form of "security via obscurity"; probably completely useless, but I know when I'm casually looking at files with which I'm unfamiliar, I tend to look at .txt files and to leave .zip files along. But you can name your file any way you want.
PS > Add-Content -Path ".\extras.zip" -Value $("SuperSecret") | ConvertFrom-SecureString -AsPlainText -Force)This step adds the username to the now-already-existing "extras.zip" file. The value that gets added to the file is the password, which before getting added to the file, is piped to a converter that takes the plain text of "SuperSecret" and converts it to a SecureString.
You should now have a file named "extras.zip" with something like the following (which you can see with "type .\extras.zip":
johndoe 01000000d08c9ddf0115d1118c7a00c04fc297eb010000001150a84662a6cd45af512286977fabcc00000000020000000000106600000001000020000000943962d675717d4a12cfe44a8a22b6a5f0ca77762c7bb61e6929515af684db5d000000000e80000000020000200000006370779b6f82983d4f417eccad5be5a80b0ae8a71d2820e736862d2b25453ce220000000c96767ea12fd28ab840fcefb49b4f86d84e97f4676e806a8d7511a86532d3c3f400000008a26d44b1d1df760436be9be186cb87a6cca6220364752e435af61188330043ac201f634ff88fd751b17aff9394c00e6ada09a2f1dd93c27702f9802e813f90dNormally the ConvertTo-SecureString expects its input to be a standard encrypted string of plain text, like the line with numbers above. But we're starting with the plain text of "SuperSecret". If you feed it this plain unencrypted text, it complains. You can see this for yourself with this command:
PS > "SuperSecret" | ConvertTo-SecureStringThat's why we use the "-AsPlainText" argument, to override that behavior. You can see this for yourself like so:
PS > 'SuperSecret" | ConvertTo-SecureString -AsPlainTextBut then you get another complaint. For security reasons, the command doesn't like to be given secret info out in the open, but you can force it to accept the input anyway, with:
PS > 'SuperSecret" | ConvertTo-SecureString -AsPlainText -Forceand now you see that the result is a SecureString.
However, we can not write this SecureString out to a file, and then recover it later. But what we can do is reverse the process, part-way. We won't reverse it back to a plain-text "SuperSecret", but rather to an encrypted-text form of "SuperSecret", by piping the above command to the counterpart:
PS > 'SuperSecret" | ConvertTo-SecureString -AsPlainText -Force | ConvertFrom-SecureString
The "native language" of these two tools is SecureStrings on one side and encrypted text on the other, so we don't have to specify anything special; it just takes the SecureString in the previous command and converts from that into encrypted text.
Now that we have the credentials stored in an external file, we have to tell our script to read them.
Reading the Credentials from an External File
Use the PSCredential Object
And now you can use the PSCredential object just as we did above:
No comments:
Post a Comment