Passwörter in PowerShell

Bei verschiedenen Gelegenheiten kann es vorkommen, dass man Passwörter in Powershell verwenden muss. Soll z.B. ein anderer Rechner heruntergefahren und für die Authentifizierung ein anderer als der aktuell angemeldete Benutzer verwendet werden, müssen Benutzername und Passwort angegeben werden. Diese können mit Get-Credential zur Laufzeit abgefragt werden:

1
2
$Credential = Get-Credential
Stop-Computer -Computer 192.168.2.10 -Credential $Credential -WhatIf

Der Benutzername kann auch vorbelegt werden, so dass statt Benutzernamen und Passwort nur noch das Passwort eingegeben werden muss:

1
2
$Credential = Get-Credential -Credential "Domain\User"
Stop-Computer -Computer 192.168.2.10 -Credential $Credential -WhatIf

Erstellt man ein Script das ohne Benutzereingaben laufen muss (z.B. ein Nagios Plugin oder Scheduled Task), kann das Passwort auch direkt im Script angegeben werden. Dort muss es allerdings in einen SecureString umgewandelt werden.

1
2
3
$SecurePassword = ConvertTo-SecureString "SecurePassword" -AsPlainText -Force
$Credentials = New-Object System.Management.Automation.PSCredential("Domain\User", $SecurePassword)
Stop-Computer -Computer 192.168.2.10 -Credential $Credential -WhatIf

Diese Variante ist jedoch ziemlich unsicher und sollte unbedingt vermieden werden. Jeder der Zugriff auf das Script hat kann auch das Passwort auslesen. Ein solches Script in öffentlichen oder halb-öffentlichen Code Repositories (z.B. GitHub) zu verwalten ist ein hohes Sicherheitsrisiko. Eine einfache Suche könnte so die Passwörter zurückgeben.

Man könnte das Passwort als Parameter übergeben. Aber auch so wird evtl. das Passwort im Klartext in einer Datei gespeichert (z.B. bei der Definition eines Nagios Checks) oder ist in der Konfiguration eines Scheduled Tasks zu sehen:

1
2
3
4
5
6
7
8
Param(
    [Parameter(Mandatory=$True)]
    [string]$Password
)

$SecurePassword = ConvertTo-SecureString $Password -AsPlainText -Force
$Credentials = New-Object System.Management.Automation.PSCredential("Domain\User", $SecurePassword)
Stop-Computer -Computer 192.168.2.10 -Credential $Credential -WhatIf

Das Passwort kann auch als verschlüsselter String in einer Datei gespeichert werden. Dafür wird das Passwort einmalig eingelesen, verschlüsselt und z.B. in der Datei password.txt abgespeichert.

1
2
3
$SecurePassword = Read-Host -AsSecureString
$EncryptedPassword = ConvertFrom-SecureString -SecureString $SecurePassword
Set-Content -Path "password.txt" -Value $EncryptedPassword

Danach kann das Passwort immer wieder aus dieser Datei eingelesen werden:

1
2
3
4
$EncryptedPassword = Get-Content -Path "password.txt"
$SecurePassword = ConvertTo-SecureString -String $EncryptedPassword
$Credentials = New-Object System.Management.Automation.PSCredential "Domain\User", $SecurePassword
Stop-Computer -Computer 192.168.2.10 -Credential $Credential -WhatIf

Für die Verschlüsselung des Passwortes wird die Windows Data Protection API (DPAPI) verwendet. Aus diesem Grund ist das verschlüsselte Passwort immer nur für den bei der Erstellung verwendeten Benutzer und Host gültig. Außerdem ist das abgespeicherte Passwort ungültig sobald sich das Passwort des Windows-Benutzers ändert. Um das Passwort unabhängig vom angemeldeten Benutzer und verwendeten Host zu machen, kann der Parameter Key oder SecureKey beim Erstellen des Passworts verwendet werden. Bei Key wird ein Schlüssel als ByteArray angegeben, bei SecureKey als SecureString. In beiden Fällen wird zusätzlich zur Datei mit dem verschlüsselten Passwort ein weitere Datei, die den verwendeten Key (im Beispiel key.txt) enthält, abgelegt.

Parameter Key

Einmaliges Erstellen eines zufälligen Keys (ByteArray):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
Function RandomByte {
     $RandomByte = @()
     For ($i=1; $i -le 16; $i++) {
     [Byte]$RByte = Get-Random -Minimum 0 -Maximum 256
     $RandomByte += $RByte
     }
     $RandomByte
}
$Key = RandomByte
$Key | out-file "key.txt"
$SecurePassword = ConvertTo-SecureString -String "SecurePassword" -AsPlainText -Force
$EncryptedPassword = ConvertFrom-SecureString -SecureString $SecurePassword -Key $Key
Set-Content -Path "password.txt" -Value $EncryptedPassword

Einlesen des Passwortes und Keys:

1
2
3
4
$EncryptedPassword = Get-Content -Path "password.txt"
$Key = Get-Content -Path "key.txt"
$SecurePassword = ConvertTo-SecureString -String $EncryptedPassword -Key $Key
$Credentials = New-Object System.Management.Automation.PSCredential "User", $SecurePassword

Parameter SecureKey

Einmaliges Erstellen eines zufälligen Keys (SecureString):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
Function RandomString {
     $RandomString = ""
     For ($i=1; $i -le 16; $i++) {
     [Char]$RChar = Get-Random -Minimum 33 -Maximum 127
     $RandomString += $RChar
     }
     $RandomString
}
$KeyString = RandomString
Set-Content -Path "key.txt" -Value $KeyString
$SecurePassword = ConvertTo-SecureString -String "SecurePassword" -AsPlainText -Force
$Key = ConvertTo-SecureString -String $KeyString -AsPlainText -Force
$EncryptedPassword = ConvertFrom-SecureString -SecureString $SecurePassword -SecureKey $Key
Set-Content -Path "password.txt" -Value $EncryptedPassword

Einlesen des Passwortes und Keys:

1
2
3
4
5
$EncryptedPassword = Get-Content -Path "password.txt"
$KeyString = Get-Content -Path "key.txt"
$Key = ConvertTo-SecureString -String $KeyString -AsPlainText -Force
$SecurePassword = ConvertTo-SecureString -String $EncryptedPassword -SecureKey $Key
$Credentials = New-Object System.Management.Automation.PSCredential "User", $SecurePassword

Fazit

Nach Möglichkeit sollte das Passwort zur Laufzeit mit Get-Credential oder Read-Host eingelesen werden. Muss das Script automatisiert werden und wird immer von dem selben Benutzer auf dem selben Host gestartet, kann das Passwort als SecureString in einer Datei abgelegt werden. Ist dies nicht möglich muss der Key zum Verschlüsseln des Passworts ebenfalls entweder mit -Key oder -SecureKey in einer zusätzlichen Datei abgelegt werden.

Auch wenn das Passwort bei den beschriebenen Verfahren nicht in Klartext gespeichert wird ist es für einen erfahrenen Benutzer unter Umständen möglich das eigentliche Passwort auszulesen. Die Dateie(en) mit den verschlüsselten Daten sollte also zusätzlich durch weitere Maßnahmen (z.B. NTFS Berechtigungen) geschützt werden.