Send-MailMessage
In PowerShell you can use the Send-MailMessage
command to simply send an email through SMTP. This is easy enough and the command is simply to call this with some arguments as follows:
1 | Send-MailMessage -SmtpServer "mail.yourserver.com" -Subject "Error" -From "user@domain.com" -To "user@domain.com" -Body "test" |
The problem is that you may get an authentication error:
The SMTP server requires a secure connection or the client was not authenticated. The server response was: 5.5.1 Authentication Required. |
The issue is that you need to specify a credential by using the -Credential
parameter
You can use the Get-Credential
Cmdlet, but this will show a prompt unless you specify the username and password in your script file. This is not good for anyone viewing or copying our script file. Turns out we can easily encrypt this information using the ConvertTo-SecureString
:
1 | Read-Host -AsSecureString |
************ System.Security.SecureString PS C:\> |
We can also read it from a UI prompt:
1 | ( Get-Credential -Credential "user" ).Password |
You can also save this password to a file like this:
1 | ( Get-Credential -Credential "user" ).Password | ConvertFrom-SecureString | Out-File "C:\temp\mycredential.cred" |
Which will produce output that looks something like this:
01000000d08c9ddf0115d1118c7a00c04fc297eb0100000064ff74aec2ba6d428b22782a6f91faac000000000200000000001066000000010000200000005b03f7a95e78f996e0a25c5b50fc3f481cf275092131e7fc45eb7553db7d1220000000000e800000000200002000000060b480288abe198159022376076f3f6663478790e547238af5d8550b65a9b6f810000000f536330ee4f0a2a2cd37da4472c4ed17400000008d65be8e08d68ce5472c25f70b60a252d9380b7e950a5780b253e1e20bccbd491bb65d5db6513b0e8925eb6e70f730e21b09223981f43f96dc2b1de12a3407fe |
Now these secure strings cannot be decoded by anyone else but your own logged on user, if you try to take this file and decrypt it on a different machine you will get this error:
ConvertTo-SecureString : Key not valid for use in specified state. At line:1 char:58 + cat ("C:\temp\mycredential.cred") | convertto-securestring <<<< + CategoryInfo : InvalidArgument: (:) [ConvertTo-SecureString], CryptographicException + FullyQualifiedErrorId : ImportSecureString_InvalidArgument_CryptographicError,Microsoft.PowerShell.Commands.ConvertToSecureStringCommand
|
Note: Initially i thought that I could make this more secure by providing an additional security key something like this:
1 | ConvertFrom-SecureString -Key ( [System.Text.Encoding] ::ASCII.GetBytes( "0e5c436f3993432590f12c0b2a48dafa" )) |
The Key i would use I would simply generate as follows:
1 | [System.Guid] ::NewGuid().ToString().Replace( '-' , '' ) |
0e5c436f3993432590f12c0b2a48dafa PS C:\> |
But this actually circumvents the Data Protection API. This page has a good explanation and more information.
So which way is more secure? Well that depends on your needs and environment but I think when the key is in your script file it is easier for an attacker to copy the file and script and then decrypt the information. So as always it is a good idea to apply as many security features as possible and keep the file safe and private, apply permissions and possibly use a different or lower access account.
The simplest way to save the credential to a file would be as follows:
1 | ( Get-Credential -Credential "user" ).Password | ConvertFrom-SecureString | Out-File c:\temp\mycredential.cred |
01000000d08c9ddf0115d1118c7a00c04fc297eb0100000021edac733db042498f873cbbdc18c766... |
AccountDomainSid
: 1 2 3 4 5 | function GetFile( $storeFolder ) { return [System.IO.Path] ::Combine( $storeFolder , [System.Security.Principal.WindowsIdentity] ::GetCurrent().User.AccountDomainSid.ToString()) } |
1 2 3 4 | function CreatePassword( $storeFolder ) { ( Get-Credential -Credential "user" ).Password | ConvertFrom-SecureString | Out-File (GetFile $storeFolder ) } |
And use it:
PS C:\> CreatePassword c:\temp |
Creates a file S-X-X-XX-XXXXXXX-XXXXXXX-XXXXXXXXX
01000000d08c9ddf0115d1118c7a00c04fc297eb0100000064ff74aec2ba6d428b22782a... |
Reading the password:
1 2 3 4 | function GetPassword( $storeFolder ) { return cat ((GetFile $storeFolder )) | ConvertTo-SecureString } |
And use it:
PS C:\> GetPassword c:\temp System.Security.SecureString |
Now as you can see we can't actually see the password because it is securely stored in the System.Security.SecureString
But we can use this object to create Credentials.
Creating a credential:
1 2 3 4 | function ReadCredential( $userName , $storeFolder ) { return new-object -typename System.Management.Automation.PSCredential -argumentlist $userName , (GetPassword $storeFolder ) } |
And use it:
PS C:\> ReadCredential "DOMAIN\user" "C:\temp" UserName Password -------- -------- DOMAIN\user System.Security.SecureString PS C:\> |
Putting it all together
As always you can create a reusable module from the functions above and use it whenever you need to save or load credentials. Just pick a filename such asCredentialFunctions.psm1
and then import it using: 1 | Import-Module -Name "c:\PowerShell\CredentialFunctions.psm1" |
And now you can simply use this in credential functions such as Send-MailMessage
1 | Send-MailMessage -SmtpServer "mail.yourserver.com" -Subject "Error" -From "user@domain.com" -To "user@domain.com" -Body "test" -Credential (ReadCredential "DOMAIN\user" "C:\temp" ) |
Powershell has a number of functions that may require Credentials and you could use this to do things like start processes etc.
Final note:
Even though the password is encrypted securely and with a key don't leave the file lying around and still protect it for instance on a flash disk. An attacker could still having access to your machine and your script key decrypt it. Lastly don't abuse the SMTP server.
No comments:
Post a Comment