PS Passwort / Kennwort

Diese Seite beschreibt die verschiedenen Möglichkeiten mit Kennworten in Powershell zu arbeiten. Spätestens wenn Sie mit PowerShell den lokalen PC verlassen und z.B.: auf ein Active Directory oder Exchange PowerShell oder andere Webservices zugreifen wollen, müssen Sie sich um die Authentifizierung Gedanken machen. Ein Skript läuft natürlich immer mit den Rechten des aufrufenden Prozesses aber die reichen vielleicht nicht immer. Selbst wenn das PowerShell-Script mit einem Domänen-Account läuft, kann es sein dass Sie für Zugriffe auf andere Dienste Credentials angeben müssen. Nicht immer ist ein Trust vorhanden, möglich oder erwünscht. Oder die Gegenstelle unterstützt einfach keine Anmeldung per Kerberos oder NTLM, sondern erwartet z.B. "BasicAuthentication" oder die Eingabe von Daten in Anmeldeformularen.

Kennworte eingeben

Wenn ein Skript interaktiv abläuft, dann kann das Kennwort per Eingabe angefordert werden. Dazu gibt es gleich drei mir  bekannte Wege

  • Get-Credential
    Wenn später als Parameter bei Commandlets gleich mit credentials gearbeitet werden kann, sollten diese direkt so eingelesen und weiter verarbeitet werden.
$cred = Get-Credentials
  • Read-Host mit asSecureString
    Einige Funktionen erwarten aber die Angabe eines Kennworts als "Secure Password". Dann kann der String z.B.: mit Read-Host eingelesen werden.

$password = read-host "Enter Password" -asSecureString

  • Read-Host als Klartext und Angabe Konvertierung
    Alternativ kann man mit den beiden Commandlets "convertto-securestring" und "convertfrom-securestring" die Werte umwandeln. Wird also bei einem Commandlet ein Parameter "Passwort" erwartet, dann hilft der geklammerte Ausdruck:

$password = (convertto-securestring -string "kennwort" -asplaintext -force)

Allerdings ist es generell eine schlechte Idee, Kennworte in einem Skript fest zu hinterlegen. Wenn Sie nicht gleich die "integrierte Authentifizierung" nutzen möchten, d.h. der gerade angemeldete Benutzer, der das Skript ausführt, dann können Sie das Kennwort auch in einer Datei (reversibel) verschlüsselt speichern und importieren. 

Kennwort speichern

Damit stellt sich natürlich wo das Kennwort hinterlegt wird, damit der Zugriff möglich ist. Verschiedene Verfahren sind für den Zugriff auf andere Dienste denkbar:

Verfahren Bewertung

Credentials des ausführenden Kontos

  • Kein Kennwort im Skript oder Konfiguration zu speichern
  • Kennwort kann im Taskplaner hinterlegt sein
  • Angesprochene Dienste müssen "Integrierte Anmeldung" unterstützen
    Kein Problem für Active Directory und Exchange Remote PowerShell im LAN aber knifflig bei Office 365 oder über diverse Proxy-Server
  • Keine Unterstützung für "BasicAuth"-Dienste

Klartext-Credential im Skript
Klartext-Credential in Konfig-Datei
Klartext-Credential als Aufrufparameter

  • Sehr einfach umzusetzen
  • Universelle verwendbar
  • Sehr unsicher

Crypted-Credential im Skript
Crypted-Credential in Konfig-Datei
Crypted-Credential als Aufrufparameter

  • Relativ einfach umzusetzen
  • Crypt-String muss erst erzeugt und dann abgelegt werden
  • nicht einfach reversibel (aber möglich)
  • Verschlüsselung durch "Key" steuerbar
  • Universell verwendbar

Token/Zertifikat

  • Unterstützung durch den Dienst erforderlich
  • Beschränkung auf den Dienst

Besonders interessant ist hier also die "verschlüsselte" Speicherung. Schauen wir uns aber erst mal die von mir am häufigsten per PowerShell angesprochenen Dienste an.

Credentials erstellen

Die verschiedenen Dienste unterstützen verschiedene Anmeldeverfahren. Gerade PowerShell-Commandlets nutzen gerne einen "-Credential"-Parameter der mit entsprechenden Daten gefüllt sein muss, so z.B. bei New-PSSession. Der einfachste Weg ist natürlich die Eingabe der Anmeldedaten mit einem Formular.

# Abfrage von Benutzer und Kennwort
$cred = get-credential

#Abfrage Kennwort fuer einen vorbelegten Benutzer
$cred = get-credential  "DomainUser"

Der Anwender kann dann das Kennwort eingeben:

Das Ergebnis ist dann ein "sicherer" credentials Container mit den Userdaten und dem "SecureString".

PS C:\> $cred | fl
UserName : Domain\User
Password : System.Security.SecureString

Der "SecureString" kann auf den ersten Blick aber nicht direkt ausgelesen oder in ein Kennwort zurück verwandelt werden. Allenfalls eine Ausgabe als "langer Text" ist möglich.

PS C:\> $cred.Password | ConvertFrom-SecureString
01000000d08c9ddf0115d1118c7a00c04fc297eb01000000d1cc4f381f4f7a4ba6b822a05720ed3
e0000000002000000000003660000c00000001000000040f15edb2a0f81f7d5b133c57a76b4da00
00000004800000a000000010000000d65b95f61f26da4051a6890ebb20e048100000001b7006495
84e8025661d3051cc5e0d25140000004e44180463eaacb947357a87d3821876a34ff8fe

Dieser String kann aber natürlich gespeichert und später wieder zurückkonvertiert werden. Sie können das Kennwort auch wieder extrahieren, zumindest wenn Sie  auf dem gleichen Computer sind, auf dem die Credentials eingegeben wurden.

$cred.GetNetworkCredential().Username
$cred.GetNetworkCredential().Domain
$cred.GetNetworkCredential().Password

Werden zwei Variablen mit den gleichen Daten gefüllt, dann sind die verschlüsselten Kennworte nicht identisch!

Der "Key"

Natürlich muss man sich als Administrator nun fragen, wie das dann funktioniert, wenn die gleichen Anmeldedaten unterschiedliche "SecureString"-Werte ergeben und wie sicher das alles ist, wo "intern" daraus doch wieder ein Kennwort werden kann. Ich habe einfach das gleiche Kennwort mehrfach konvertiert und ausgeben lassen

convertto-securestring "P@ssW0rD!" -asplaintext -force |convertfrom-securestring

Interessanterweise liefern die Ausgaben immer unterschiedliche Werte

Sie können auch jeden der Werte wieder mit "Convertto-SecureString umwandeln und die Länge passt. PowerShell bedient sich dabei der Windows Data Protection API (DPAPI https://msdn.microsoft.com/en-us/library/ms995355.aspx), d.h. diese sicheren String können nur auf dem gleichen PC mit dem gleichen Benutzerkonto wieder zurückverwandelt werden. Der erforderliche Schlüssel wird aus diesen beiden Informationen (Benutzer und Computer) generiert. Sie können aber auch einen anderen Schlüssel mit angeben. Dann wird die Information "portabel". Sie müssen nun allerdings auf den verwendeten Schlüssel aufpassen.

Password in Datei

Mit dem Trick und der Sicherheit, dass diese Zahlenfolge nur auf dem gleichen PC mit dem gleichen Benutzer wieder verwendet werden kann, lässt sich sehr einfach auch ein Kennwortsafe auf Dateibasis aufbauen.

# Kennwort interaktiv erfragen und als Datei abspeichern
read-host "Enter Password" -asSecureString | ConvertFrom-SecureString | Out-File "C:\temp\Passwordstore.txt"

Der umgekehrte Weg ist etwas aufwändiger, da das Kennwort so alleine nicht unverschlüsselt vorliegt. Aber es kann ein Credential Container befüllt werden

# Kennwort aus Datei auslesen und als SecureString ablegen
$pass = (get-content "C:\temp\Passwordstore.txt" | convertto-securestring)

# Credentials Container mit Kennwort laden. Der Username ist nicht relevant
$cred = new-object -typename System.Management.Automation.PSCredential -argumentlist "Username",$pass

# Kennwort im Klartext anzeigen
$cred.GetNetworkCredential().password

Normalerweise muss ich das Kennwort aber nicht mehr als Klartext anzeigen, wenn ich es schon in $cred stehen habe. Diese Variable kann ich mit dem richtigen Benutzernamen an vielen Stellen schon direkt für die Authentifizierung einsetzen.

Weitere Links