Passwort sicher in Powershell Scripts speichern
Es gibt verschiedene Anwendungsfälle in denen man ein Passwort, wenn möglich sicher, in einem Powershell Script speichern muss. Beispielsweise wenn es sich dabei um ein Automatisierungsscript handelt, welches per Task Scheduler ausgeführt wird. Doch wie kann man ein Passwort mit gutem Gewissen in ein Script einbauen, das womöglich auf verschiedenen Systemen läuft? In diesem Artikel möchte ich gerne ein paar der Möglichkeiten behandeln.
- Passwort per Get-Credential manuell eingeben
- Passwort in Klartext speichern
- Passwort mittels Windows Data Protection API speichern
- Passwort mittels AES Key speichern
Disclaimer: Die nachfolgenden Informationen beziehen sich, wie nahezu immer auf diesem Blog, aus meinem eigenen Erfahrungsschatz. Falls weitere Methoden bekannt sind, können diese gerne in den Kommentaren ergänzt werden.
Passwort per Get-Credential eingeben
Dabei handelt es sich bereits um die sicherste Variante, welche angewendet werden kann. Das Passwort wird beim Aufruf des Scripts vom User manuell eingegeben. Mittels Get-Credential erzeugt man einen sicheren Powershell Container, in der das Passwort vom Benutzer eingegeben werden kann.
Write-Host "Bitte geben Sie die Benutzerdaten ein" $credentials = Get-Credential -Credential Administrator
Dabei werden die Benutzerdaten in der Variabel $credentials gespeichert und nach Ausführung des Scripts wieder verworfen. Somit ließen sich die Anmeldedaten nur in dieser Session auslesen. Leider ist diese Variante für automatisierte Scripts unbrauchbar, da eine Interaktion mit dem Benutzer nötig ist.
Anwendungsfall: bei manueller Ausführung des Scripts
Passwort in Klartext speichern
Die zweite und wohl auch unsicherste Variante wäre das Passwort in Klartext zu speichern. Somit würde man zwar im ersten Schritt eine Automatisierung des Scripts ermöglichen, teilt allerdings auch allen Personen, die Zugriff auf das Script haben, das Passwort mit.
$password = "Test1234" | ConvertTo-SecureString -AsPlainText -Force $username = "Administrator" $credentials = New-Object System.Management.Automation.PSCredential (“username”, $password)
Hierbei wird das Passwort zuerst in einen SecureString konvertiert, denn nur so kann es in einem Powershell Credentials-Object verwendet werden. Sicherheitstechnisch natürlich brandgefährlich.
Anwendungsfall: Testzwecke und nicht produktive Scripts
Passwort mittels Windows Data Protection API speichern
Nun kommen wir schon zu einer brauchbaren Lösung. Dabei hilft uns die Windows DPAPI (Windows Data Protection API). Sie speichert uns das Kennwort in einem sicheren SecureString ab, welcher nur auf jenem Computer und jenem Benutzerkonto entschlüsselt werden kann, welcher ihn verschlüsselt hat.
### Step 1: Creating Securestring $password = Read-Host -AsSecureString $password | ConvertFrom-SecureString | Out-File .\Desktop\password.txt ### Step 2: Using Securestring in Scripts $username = "Administrator" $password = Get-Content .\Desktop\password.txt | ConvertTo-SecureString $credentials = New-Object System.Management.Automation.PSCredential (“$username”, $password)
- 1. Schritt: Das Passwort wird vom Benutzer mittels Read-Host eingegeben und anschließend in ein Textdokument am Desktop gespeichert. Dabei wird das Passwort natürlich nicht im Klartext, sondern in einer langen Zeichenkette von Zahlen und Buchstaben abgelegt. Dieser Schritt ist nur einmalig notwendig. Bei allen zukünftigen Scripts muss das Textdokument einfach nur eingelesen werden.
- 2. Schritt: Das zuvor exportierte, verschlüsselte Password wird wieder eingelesen und in der Variabel $password gespeichert. Anschließend kann wieder wie gewohnt ein PSCredential-Object verwendet werden.
Diese Variante ist weitestgehend sicher, wobei natürlich niemals eine 100% Sicherheit gewährleistet werden kann. Dadurch, dass die Windows Data Protection API von Microsoft zum Tragen kommt, kann das Passwordfile nur von dem gleichen PC und dem gleichen Benutzer entschlüsselt werden. Und damit kommt auch schon das größte Problem zum Vorschein. Wenn man das Script auf einem anderen PC ausführen möchte, müsste man zuvor das Passwortfile auf dem jeweiligen Computer auf diese Weise verschlüsseln und ersetzen. Meist führt man Scripts im Task Scheduler auch mit einem anderen Benutzer aus, als man es geschrieben hat. Auch dann würde die Verschlüsselung nicht mehr funktionieren, denn zum Einlesen muss immer der gleiche Computer und gleiche Benutzer verwendet werden, der es auch verschlüsselt hat.
Verwendest du den Code immer auf dem gleichen PC, wäre das wohl die optimale Variante für dich. Solange der Angreifer keinen Zugang zu deinem PC und deinem Benutzerkonto hat, ist die Verschlüsselung sehr sicher. Auch wenn er Zugriff auf das Passwort-File bekommen würde, müsste dieser erst via Brute Force den String decrypten – und das ist glücklicherweise so gut wie unmöglich.
Anwendungsfall: Script wird immer am gleichen Computer ausgeführt
Passwort mittels AES Key speichern
Um das in Variante 3 genannte Problem zu adressieren, kann das Passwort mittels einem zuvor generierten AES-Key verschlüsselt werden. Somit kann das Script auf allen PCs mit allen Benutzerkonten ausgeführt werden, auf denen sich auch der AES-Key befindet.
### Step 1: Creating Securestring encrypted with AES Key $AESKey = New-Object Byte[] 32 [Security.Cryptography.RNGCryptoServiceProvider]::Create().GetBytes($AESKey) $AESKey | Out-File .\Desktop\aes.key $password = Read-Host -AsSecureString $password | ConvertFrom-SecureString -Key $AESKey | Out-File .\Desktop\password.txt ### Step 2: Using Securestring and AES Key in Scripts $username = "Administrator" $AESKey = Get-Content .\Desktop\aes.key $password = Get-Content .\Desktop\password.txt | ConvertTo-SecureString -Key $AESKey $credentials = New-Object System.Management.Automation.PSCredential (“$username”, $password)
- 1. Schritt: Es wird ein 32 Byte (256bit Verschlüsselung) langes Array erstellt, welches mit zufälligen Zahlen befüllt wird. Dieses Array ist unser AES-Key und wird als „aes.key“ exportiert. Anschließend lesen wir das Passwort mittels Read-Host ein und verschlüsseln es zugleich mit dem zuvor generierten Key. Dieser Schritt muss nur zur erstmaligen Generierung des Passworthashs und des Schlüssel durchgeführt werden.
- 2. Schritt: Die zuvor erstellten Files werden in das Script eingelesen und als PSCredential-Object gespeichert.
Auch diese Variante ist sehr sicher, solange man den AES-Key sicher aufbewahrt. Sobald sich ein Angreifer Zugriff zum Key verschafft hat, ist er auch in der Lage, das Passwort zu entschlüsseln. Daher ist es bei dieser Variante essentiell, den Key mittels NTFS Berechtigungen so gut als möglich zu verwahren. Würde man das File öffentlich zugänglich machen, könnte man das Passwort auch gleich im Klartext speichern. Es würde keinen großen Unterschied machen.
Anwendungsfall: Script wird auf unterschiedlichen Computer ausgeführt
Fazit
In meinem Fall müsste ich ein Automatisierungsscript auf verschiedenen Systemen speichern, auf die teilweise auch Kunden Zugriff haben. Welche Variante würde man in so einem Fall wählen? Die traurige Antwort: Keine! Keine der vier Möglichkeiten ist brauchbar. Für alle jene, die nicht wissen warum, hier noch die kurze Erläuterung:
- Variante 1: Damit lässt sich leider keine Automatisierung ermöglichen. Somit fällt diese Methode komplett weg.
- Variante 2: Ich denke das dürfte soweit verständlich sein. Für Testzwecke allerdings die einfachste Methode.
- Variante 3: Es wäre umständlich, auf jedem Kundensystem ein neues Passwortfile zu generieren. Außerdem könnte der potentielle Angreifer mittels Domain Administratorenrechten einfach das Passwort meines AD-Users zurücksetzen und sich damit Zugang zu meinem Benutzerkonto gewähren. Somit wäre er auch in der Lage das Passwort zu encrypten.
- Variante 4: Man müsste den AES-Key irgendwo auf dem Kundensystem ablegen. Da dieser allerdings wie bereits erwähnt Adminrechte besitzt, hat er dementsprechend auch Zugriff auf das gesamte Dateisystem.
Kann man die Variante 1 und 4 nicht kombinieren? Wenn man z.B eine Sicherung am gleichen PC automatisch verschlüsseln oder wiedereinlesen möchte kommt Var.4 zum Einsatz. Findet das Scrit die Daten für var.4 nicht, kann die Entschlüsselung auch mit Var.1 mittels manueller Passwort-Eingabe durchgeführt werden. Damit sind für viele Szenarien die Vorteile beider Varianten genutzt und die Nachteile umgangen.
Hallo Bernd,
ja natürlich – die Option 1 lässt sich immer in das Script integrieren. Möchte ich das Script automatisiert um 2 Uhr nachts laufen lassen, muss ich aber trotzdem zwangsläufig den AES Key am Computer abspeichern – oder man müsste manuell das Kennwort eingeben. Leider gibt es keine perfekte Lösung für ein solches Szenario – es gibt immer Abhängigkeiten, die ein Problem darstellen könnten.
Ich habe nun alle Varianten ausprobiert. In Powershell funktionieren auch alle Varianten einwandfrei. Ich verwende die Credentials, um automatisiert die Azure AD auszulesen und auf dem SQL Server wegzuschreiben. Nun stoße ich aber jedes Mal auf das gleiche Problem: dieses Vorgehen MUSS auf dem SQL Server in einer Stored Procedure laufen. Per xp_cmdshell (EXEC xp_cmdshell ‚powershell „C:\[…]\Test_AD.ps1″‚) läuft das Ganze im Prinzip auch korrekt. Allerdings erhalte ich weder mit Variante 3, noch mit Variante 4, Zugriff auf das Password-File und somit bleibt $password leer. Folgende Fehlermeldung erscheint im Ergebnis-Fenster: Get-Content : Zugriff verweigertIn C:\Yasin\Test_AD.ps1:12 Zeichen:11+ $AESKey = Get-Content C:\Users\Yasin\Desktop\aes.key+ CategoryInfo:… Weiterlesen »
Ich hatte aktuell noch nicht den Anwendungsfall eine Powershell mittels einer MSSQL Stored Procedure aufzurufen allerdings deutet zumindest die Fehlermeldung darauf hin, dass jener User, unter der dein MSSQL Service läuft, keine Berechtigung hat, auf die Datei „C:\Users\Yasin\Desktop\aes.key“ zuzugreifen. Mit welchen Benutzer läuft dein MSSQL Service? SYSTEM oder vielleicht ein User ohne Administratorenrechten?
Das hat nun tatsächlich funktioniert, vielen Dank für den Tipp! Falls jemand den gleichen Anwendungsfall haben sollte: MSSQL-User auslesen (ich habe das per Abfrage gemacht “ xp_cmdshell ‚whoami.exe‘ „. Geht auch über Dienste -> SQL Server (MSSQLSERVER) -> Reiter „Anmelden als“ -> „NT Service\MSSQLSERVER“ ist der User Diesen kopieren und im Anschluss auf die entsprechenden Dateien, oder gleich dem ganzen Ordner Lese-Berechtigung geben (reicht aus für diesen Fall) Rechtsklick auf das Ordner -> Eigenschaften Reiter „Sicherheit“ -> „Bearbeiten“ -> „Hinzufügen“ „Objekttypen…“ anklicken -> Alle Elemente auswählen (Dienstkonto wichtig!) -> mit OK bestätigen „Pfade…“ anklicken -> Oberstes Element auswählen und mit… Weiterlesen »
Freut mich zu hören, dass es funktioniert hat! 🙂
Sicherheit ist ein trugschluss. Letztlich kann man es einem Angreifer nur so schwer wie möglich machen. Seit Powershell 5/7 gibt es zum Abspeichern von Passwortdaten auch Passwort-Safe-Anbindungen, beispielsweise für: – SecretStore (Microsoft) – KeePass – LastPass – Hashicorp Vault – KeyChain – CredMan – Azure KeyVault (Microsoft Owned) Kombiniert mit den hier genannten Verschlüsselungen und vielleicht noch eigenen Verschlüsselungsmethoden kann man es einem Angreifer um einiges schwerer machen. Da wir hier aber von Scripts sprechen, ist die Sicherheit immer problematisch. Eine kurze Änderung des Scripts und schon hat… Weiterlesen »
Absolut korrekt. Hier greift, wie so oft, auch das Zero Trust Model. Eine 100% Sicherheit gibt es leider nicht.
Hi vielen Dank für die Anleitung!
Eine weitere Möglichkeit sind Umgebungsvariablen, damit sind Passwörter zumindest nicht im Klartext direkt im Script