PowerShell und Mail

Über das Commandlet "Send-MailMessage" ist es in PowerShell sehr einfach, eine SMTP-Nachricht zu versenden. Mit PowerShell 6 rät Microsoft aber von dem Commandlet ab, da es "keine sicheren Verfahren" unterstützt. Das war der Anlass, diese Seite zu erweitern.

Versand mit Send-Mailmessage

Der Bedarf, eine Mail aus einem Programm oder Skript zu senden, ist schon viele Jahre vorhanden. Es ist eine einfache Möglichkeit, wie ein Programm eine Rückmeldung oder Fehlermeldung absetzen kann, die dann weiter verarbeitet werden kann. Auf nahezu jedem Betriebssystem gibt es daher eine Funktion oder Programm, um eine Mail zu senden. Selbst unter Windows 3.1 gab es schon Blat als einfach ausführbare EXE, um per SMTP eine Mail zu senden. Unter Unix ist SENDMAIL gut bekannt und mit der PowerShell hat Microsoft das Commandlet "Send-MailMessage" bereit gestellt.

Achtung: Mit der neuen PowerShell ist der Befehl abgekündigt. Sie bekommen die Meldung

WARNING: The command 'Send-MailMessage' is obsolete. This cmdlet does not guarantee secure connections to SMTP servers. While there is no immediate replacement available in PowerShell, we recommend you do not use Send-MailMessage at this time. See https://aka.ms/SendMailMessage  for more information.

Früher waren Mailserver auch freundlich und haben von jedem System einfach Mails angenommen und das gilt heute auch für aus dem Internet erreichbare Mailserver so, dass anonyme Verbindungen angenommen werden. Allerdings haben Spam und Viren dazu geführt, dass Mailserver schon etwas genauer hinschauen, wer hier mit welcher Absenderadresse Mails einliefert. Auch Exchange verweigert seit Exchange 2007 die Annahme von anonymen Verbindung in der Standardkonfiguration.  Das ist für Send-MailMessage aber kein Problem, da es per per Default sich immer mit dem aktuell angemeldeten Benutzer, der das Kommando ausführt, anmelden will: Im LAN funktioniert das natürlich, zumindest solange Send-MailMessage die richtige "Absenderadresse" nutzt. Hier ein Wireshark Mitschnitt des folgenden Aufrufs (Domains unkenntlich gemacht)

Send-MailMessage `
   -From frank.carius@example.com `
   -To frank.carius@example.com `
   -Subject "Test" `
   -SmtpServer 192.168.100.33

Im Wireshark sehen Sie neben dem TCP-Handshake und dem SMTP-Handshake auch den AUTH.

Nur wenn die Gegenseite kein AUTH anbietet, dann sendet Send-MailMessage die Nachricht sofort anonym. Das kann ich mit meinem SMTP Blackhole einfach nachstellen. Es bietet kein AUTH an und direkt nach dem EHLO geht es mit einem "MAIL FROM" weiter:

Wenn die Gegenseite aber AUTH anbietet und Sie dennoch "anonym" senden wollen, dann kann es funktionieren, wenn Sie einen ungültigen Benutzernamen und Kennwort verwenden und damit die Verwendung der aktuellen Credentials des angemeldeten Benutzers unterbinden:

$Username = "anonymous"
$Password = ConvertTo-SecureString -String "anonymous" -AsPlainText -Force
$Cred = New-Object System.Management.Automation.PSCredential($Username,$Password)
Send-MailMessage -to "User1 <User1@msxfaq.net>" -from "Extern <advis12@example.com>" -subject "It's working! EOM" -credential $Cred

In dem Beispiel nutzt Send-Mailmessage dann eben die Anmeldung als User "anonymous" mit dem Kennwort "anonymous". Die meisten Mailserver nehmen die Mail dennoch an, aber verzögern dies etwas. Hier ein Wireshark dazu:

Das Packet 7-11 zeigt die versuchte Anmeldung per NTLM und die Ablehnung im Pakt 11 wird vom Mailserver ca. 5 Sek verzögert. Allerdings hindert das Send-Mailmessage nicht, es weiter zu versuchen und viele  Server nehmen die Mail dann auch an. Allerdings sehen sie in Outlook dann auch den Unterschied:

Die authentifizierten Mails bekommen natürlich auch den "Displaynamen" angezeigt, während die anonym eingelieferten Mails die SMTP-Adresse anzeigen. Das funktioniert aber nur, solange der Absender nicht einen Displaynamen mitliefert. Ich kann ja auch folgende Parametrisierung nutzen:

PS C:\> Send-MailMessage -From "Frank <user1@msxfaq.de>" ...

Dann zeigt Outlook eben nur den "Frank" in der Übersicht an. In der geöffneten Mail ist dann aber bei anonym eingelieferten Mails zusätzlich auch die SMTP-Adresse sichtbar. Darauf achten aber die meisten normalen Anwender natürlich nicht mehr

Sicherheit mit Send-MailMessage

Spätestens wenn Sie mal mit PowerShell Core (Version 6 und neuer) arbeiten, werden Sie eine Warnung finden.

Send-Mailmessage wurde von Microsoft noch nicht um "STARTTLS" oder SMTPS erweitert und ich habe den Anschein, dass dies auch nicht weiter geplant ist. Das ist eigentlich schade aber andere Aufgaben sind vermutlich dringender. Send-MailMessage nutzt im Unterbau ja die vorhandene ältere Komponente "SmtpClient" des .NET Framework, die das nicht kann. Interessant finde ich, dass Microsoft in der Warnung auf eine verkürzte URL bei Microsoft hinweist, die direkt auf GitHub führt:

Beim der Suche nach SMTPClient ist auch auf der Verweis auf die veraltete API zu finden


Quelle: https://docs.microsoft.com/de-de/dotnet/api/system.net.mail.smtpclient?view=netframework-4.8

Microsoft empfiehlt hier also zukünftig die Verwendung anderer Libraries.

Ich finde das sehr schade, dass eine Basisfunktion wie der Versand einer Mail zukünftig nicht mehr über ein allgemein vorhandenes Commandlet mit der entsprechenden Sicherheit möglich ist. So muss dann wieder jeder Entwickler eigene Module entwickeln oder fremde Module einbinden.

Die empfohlene Library "MailKit" stellt sich aber als sehr interessant dar. Sie liegt auf GitHub https://GitHub.com/jstedfast/MailKit und ist nicht nur ein SMTP-Client sondern zugleich auch POP3 und IMAP4 Client, der natürlich SSL und STARTTLS unterstützt. Es ist das persönliche Projekt des Autors Jeffrey Stedfast, der auch hinter dem Mailparser MimeKit (http://www.mimekit.net) steht. Ich kann nur hoffen, dass genug Personen und Firmen ihn bei seiner Entwicklung unterstützen.

Leider ist die Umsetzung natürlich nicht ganz so einfach wie ein PowerShell Einzeiler.

Aber per PowerShell bekommt man auch das hin. Wer aber eh nur "intern" mal schnell eine Mail an den eigenen Mailserver am besten Anonym senden will, kann weiterhin mit Send-MailMessage arbeiten und die Warnung einfach ignorieren.

Versand mit system.net.mail

Sie können aber immer noch die im .Net Framework eingebaute Klasse nutzen.

$MailMessage = new-object system.net.mail.MailMessage(`
   "user1@msxfaq.de",`
   "use1@msxfaq.de",`
   "Test-header-flag",`
   "Body")
$client = New-Object system.net.mail.SmtpClient("localhost")
$client.Send($MailMessage)

Das ist natürlich nicht so schön wie ein Commandlet. Auch ist die "System.Net.Mail.SmtpClient"-Klasse ebenfalls als "obsolet" gekennzeichnet.


Quelle: https://learn.microsoft.com/en-us/dotnet/api/system.net.mail.smtpclient?view=net-8.0#remarks

Weitere Links