Passwortsicherheit in PowerShell-Skripten

Nachdem ich dem Thema Sicherheit und Passwörter bereits den einen oder anderen Blogartikel gewidmet habe, möchte ich mich heute mit dem Thema “Passwortsicherheit in PowerShell-Skripten” auseinandersetzen. Dieses Thema scheint bei vielen PowerShell-Programmierern (und auch bei Developer anderer Programmiersprachen) noch nicht angekommen zu sein, wenn man sieht, wie mit Passwörtern oder anderen schützenswerten Objekten umgegangen wird.

image

Nachdem ich auch mit mehreren Kunden und Kollegen über dieses Thema bereits Diskussionen geführt habe, habe ich mich zu diesem Artikel entschlossen.

Vorbemerkung

Die Prozesse im Umgang mit Passwörtern sind, speziell bei großen Unternehmen mit hohen Sicherheitsanforderungen z. B. wie in Bankkonzernen, exakt definiert. Während meiner Zeit als IT-Consultant habe ich so manche dieser Prozessbeschreibungen einsehen und mit der Realität vergleichen können. Aufgrund der Komplexität der Prozesse suchen die Anwender (insbesondere Administratoren) jedoch oft für sich selbst gangbare Wege, die dann z. B. so aussehen:

  • Textdatei/Excel-Datei mit der Liste benötigter Benutzerkonten und Kennwörter im Netzwerk
  • Speicherung aller Dienstkonten inkl. Passwörter auf einem persönlichen iPad
  • Passwort-Manager lokal auf dem Notebook, im Unternehmensnetzwerk oder in der Cloud

Ich möchte diese Lösungen nicht beurteilen oder deren Sicherheit bewerten, denn sie ermöglichen für die betroffenen Personen oft ein effektiveres Arbeiten. Das es im Windows-Umfeld und speziell bei der Arbeit mit PowerShell jedoch bessere Möglichkeiten gibt, möchte ich mit diesem Blogbeitrag aufzeigen.

Aufgabenstellung

Bei einigen meiner bisherigen Projekte als IT-Consultant bestand die Aufgabe, mit PowerShell bestimmte Aktionen unter einem speziellen Benutzerkonto durchzuführen (ohne mit diesem lokal angemeldet zu sein) oder Benutzer-Anmeldeinformationen (Credentials; Benutzername + Kennwort) zu setzen z. B. für das SQL Server sa-Benutzerkonto während der Installation oder eine Passphrase für eine SharePoint-Farm zu generieren bzw. zu speichern oder oder oder…

Die Herausforderungen bestanden bei diesen Projekten darin, Lösungen bereitzustellen, die auch hohen Sicherheitsanforderungen gerecht werden. In Abstimmung mit meinen Kunden habe ich verschiedene Lösungen implementiert, die teilweise in diesen Blog-Artikel eingeflossen sind.

Einleitung

Bei der Arbeit mit PowerShell-Skripten steht man oft vor der Herausforderung, Kennwörter zur Authentifizierung eines Benutzers für den Zugriff auf Systeme oder andere nicht öffentliche Zeichenfolge zu verwenden. Nun kann man entweder eine solche Zeichenkette fest in ein PowerShell-Skript einbauen:

$pwd = "Streng#123!Geheim"

oder man fordert den Benutzer des PowerShell-Skripts zur Eingabe des Kennworts auf:

$pwd = Read-Host -Prompt "Geben Sie bitte das Passwort ein:"

Die erste Lösung ist nicht besonders sicher: Da PowerShell-Skripte im Klartext gespeichert werden, kann jeder, der Zugriff auf das PowerShell-Skript hat, dieses lesen. Die zweite Lösung ist speziell bei vollautomatisierten Skripten, die im Hintergrund ausgeführt werden müssen, nicht anwendbar.

In dem folgenden Artikel möchte ich Ihnen deshalb sinnvolle Wege im Umgang mit Passwörtern in PowerShell-Skripten und deren Vor- und Nachteile aufzeigen.

Um Passworte zur Benutzung in einem PowerShell-Skript zu schützen, gibt es verschiedene Wege (ohne Anspruch auf Vollständigkeit):

  • SecureString/Zertifikate
  • Password Vault ab Windows 8 / Azure
  • Active Directory und Azure Right Management Service (DRM)
  • SQL Server Encryption
  • Dateiebene (NTFS-Berechtigung, Verschlüsselung)

Diese möchte ich Ihnen im Folgenden ausführlicher erklären.

SecureString

In Windows existiert die Klasse SecureString im .NET Framework (ab Version 2.0) und befindet sich im Namespace “System.Security”. Diese Klasse wandelt eine Zeichenfolge in einen verschlüsselten String um.

Ein SecureString-Objekt ähnelt einem String-Objekt darin, dass es einen Textwert besitzt. Der Wert eines SecureString-Objekts wird jedoch automatisch verschlüsselt und kann geändert werden, solange er von der Anwendung nicht als schreibgeschützt markiert wurde. Außerdem kann er von der Anwendung oder dem Garbage Collector von .NET Framework aus dem Arbeitsspeicher gelöscht werden.

Für die Verwendung von SecureStrings sind bereits einige CmdLets in PowerShell implementiert:

  • Read-Host -AsSecureString

    Eingabe einer Zeichenfolge über die Tastatur (Read-Host) und automatische Konvertierung dieser Zeichenfolge in einen SecureString (Parameter -AsSecureString)

  • ConvertTo-SecureString

    Konvertieren einer Zeichenfolge in einen SecureString.

  • ConvertFrom-SecureString

    Konvertieren eines SecureString in eine lesbare Zeichenfolge, jedoch nicht der Originaltext

  • Get-CredentialDialoggesteuerte Eingabe von Benutzername und Kennwort

Ohne spezielle Parameter verwenden die Cmdlets die Windows Sicherheit (Data Protection application programming interface, DPAPI). Jedoch lassen sich diese CmdLets und die zugrundeliegende Windows API mit höherer Sicherheit und mehr Möglichkeiten auch von der PowerShell aus verwenden. Schauen wir uns im Folgenden diese Cmdlets etwas genauer an. Doch bevor ich dazu komme, möchte ich Ihnen noch ein paar zusätzliche Informationen zu dem SecureString, Verschlüsselungsverfahren und dem DPAPI liefern.

Einleitung

Für die Verschlüsselung von Zeichenketten als SecureString erzeugt das Betriebssystem verschiedene Schlüssel, die letztendlich von den Anmeldeinformationen des Benutzers abgeleitet und im Benutzerprofil gespeichert werden. Bei lokalen Benutzerprofilen werden auf jedem System eigene Schlüssel generiert, so dass ein SecureString jeweils nur auf dem System gültig sind, auf dem sie geschaffen wurden. Durch die Speicherung der Schlüssel im servergespeicherten Benutzerprofil lassen sich gespeicherte SecureString jedoch auch auf anderen Systemen verwenden.

Ohne diese Schlüssel können die SecureString nur mit sehr hohem Aufwand entschlüsselt werden. Selbst wenn ein anderer Benutzer Zugriff auf die von Ihnen gespeicherten SecureString-Dateien erlangt, sind diese für ihn wertlos. Die Sicherheit der SecureString ist eng mit der ihres Benutzers verbunden. Wird ein Benutzerkonto kompromittiert, kann der Angreifer auch die gespeicherten SecureString verwenden, um beliebige Kommandos auszuführen. Achten Sie also auf die Einhaltung von grundlegenden Sicherheitsaspekten – das gilt selbstverständlich auch dann, wenn Sie keine SecureString verwenden.

Hinweise: Eine Instanz der System.String-Klasse ist unveränderlich und kann, wenn sie nicht mehr benötigt wird, nicht programmgesteuert für die Garbage Collection (Aufräumen des Arbeitsspeichers) geplant werden. Dies bedeutet, dass die Instanz nach dem Erstellen schreibgeschützt ist und dass nicht vorhersagbar ist, wann die Instanz aus dem Arbeitsspeicher des Computers gelöscht wird. Wenn ein String-Objekt vertrauliche Informationen wie Kennwörter, Kreditkartennummern oder persönliche Daten enthält, besteht daher ein Risiko, dass nach der Verwendung auf die Informationen zugegriffen werden kann, da die Anwendung die Daten nicht aus dem Arbeitsspeicher löschen kann.

Symmetrische Verschlüsselungsverfahren

Beim symmetrischen Verschlüsselungsverfahren (Secure Key wird die Nachricht mit dem selben Schlüssel ver- und entschlüsselt.

image

In .NET Framework sind folgende symmetrische Verschlüsselungsverfahren integriert:

Die Klassen, die von der SymmetricAlgorithm-Klasse abgeleitet werden, verwenden den CBC-Verkettungsmodus (Cipher Block Chaining), der für die kryptografische Datentransformation einen Schlüssel (Key) und einen Initialisierungsvektor (IV) erfordert. Um Daten zu entschlüsseln, die mit einer der SymmetricAlgorithm-Klassen verschlüsselt wurden, müssen Sie die Key-Eigenschaft und die IV-Eigenschaft auf dieselben Werte festlegen, die für die Verschlüsselung verwendet wurden. Wenn ein symmetrischer Algorithmus sinnvoll verwendet werden soll, dürfen nur der Absender und der Empfänger den geheimen Schlüssel kennen.

Alle kryptografischen Klassen in .NET Framework, die vertrauliche Daten enthalten, implementieren eine Clear-Methode. Bei einem Aufruf überschreibt die Clear-Methode alle vertraulichen Daten innerhalb des Objekts mit 0 (null) und gibt das Objekt anschließend frei, sodass es der Garbage Collection sicher durchgeführt werden kann. Nachdem das Objekt mit 0 (null) aufgefüllt und freigegeben wurde, müssen Sie die Dispose-Methode mit dem auf True festgelegten disposing-Parameter aufrufen, um alle dem Objekt zugeordneten verwalteten und nicht verwalteten Ressourcen freizugeben.

Inzwischen haben symmetrische Verschlüsselungsverfahren auch in Bereichen Einzug gehalten, an die man nicht so ohne weiteres denken mag und die nicht immer optimal implementiert sind. Dazu einige Beispiel, die ich bei meiner Recherche zu diesem Artikel gefunden habe:

Wer mehr zu diesem Verfahren wissen möchte, findet hier einen sehr umfangreichen Artikel (in deutscher Sprache).

Asymmetrische Verschlüsselungsverfahren

Beim asymmetrischen Verschlüsselungsverfahren gibt es zwei verschiedene Verschlüsselungsalgorithmen – einen zum Verschlüsseln und einer zum Entschlüsseln. Anders als beim symmetrischen Verschlüsselungsverfahren kann man mit dem Verschlüsselungsalgorithmus die verschlüsselte Nachricht also nicht wieder entschlüsseln. Dafür wird ein andere ergänzender Verschlüsselungsalgorithmus benötigt.

Der Schlüssel zum Verschlüsseln ist öffentlich und kann von jedem eingesehen werden. Dieser kann beispielsweise bei einer Zertifizierungsstelle hinterlegt werden. Der Schlüssel zum Entschlüsseln hat nur der Empfänger der Nachricht. So können Hacker zwar verschlüsselte Nachrichten abfangen, allerdings sind diese für sie unbrauchbar.

Der Nachteil dieses Verfahren besteht in der nur begrenzten Rechenleistung verschiedenster Computer. Es ist bis zu einem Faktor von ca. 1.000 Mal schneller Dateien mit dem symmetrischen Verschlüsselungsverfahren zu arbeiten als mit einem asymmetrischen.

Asymmetrische Verschlüsselungsverfahren kommen bei Verschlüsselungen, Authentifizierungen und der Sicherung der Integrität zum Einsatz. Einige bekannte Beispiele sind:

  • Internet- und Telefonie-Infrastruktur: X.509-Zertifikate
  • Übertragungs-Protokolle: IPSec, SSL/TLS, SSH, WASTE
  • E-Mail-Verschlüsselung: PGP, S/MIME
  • RFID Chip auf dem deutschen Reisepass
  • Electronic Banking: HBCI

image

In .NET Framework sind folgende asymmetrische Verschlüsselungsverfahren integriert:

Aufgrund der immer stärken werdenden Rechenleistung moderner Computersysteme wurden 2013 für das RSA-Verfahren Schlüssel mit einer Länge von 1024 bit empfohlen. 2014 lag die Empfehlung bereits bei 2048 bit (600 Byte). (Quelle: Bundesnetzagentur)

Doch auch große Schlüssel schützen nicht vor Implementierungsfehlern (dazu einige Links):

Hashing

Kryptologisches Hashing ist kein Verschlüsselungsverfahren, sondern dient primär zum Vergleich von Zeichenketten. Nehmen wir dazu folgendes Beispiel: Sie möchten von einem Anwender eingegebene Kreditkarteninformationen so speichern, dass diese nur mit hohem Aufwand reproduzierbar sind. Meldet sich jedoch der Anwender mit seinen Kreditkarteninformationen am System an, so soll er Zugriff auf sein Konto erhalten. Nun können Sie die Kreditkarteninformationen entweder verschlüsselt z. B. in einer Datenbank ablegen oder sie berechnen eine eindeutige Zahl (Hashwert), die anstelle der Kreditkarteninformationen gespeichert wird. Meldet sich in diesem Fall der Anwender am System an, so brauchen Sie aus den eingegebenen Daten nur den Hashwert zu berechnen und diesen mit den gespeicherten Informationen zu vergleichen.

Typische Einsatzgebiete für Hashing ist z. B. Passworte in Windows, SQL Server und anderen Systemen, Signieren (auch von PowerShell-Skripten) oder Vergleich von Dateien (https://support.microsoft.com/de-de/kb/889768).

Auch wenn es sich bei diesem Verfahren nicht um die Verschlüsselung von Daten handelt, kommt es trotzdem oft auch in Verbindung mit Verschlüsselungsverfahren vor

Data Protection application programming interface

DPAPI wurde als Komponente von Windows 2000 eingeführt und hat bis in die aktuellen Windows Versionen einen festen Platz und wird in vielen Microsoft Produkten (EFS, IIS, IE, Outlook, Microsoft Owin/Katana um nur einige zu nennen) zum Schutz von Daten verwendet.

Bei dieser Schnittstelle handelt es sich um ein symmetrisches Verschlüsselungsverfahren. Für die Verschlüsselung erzeugt das Betriebssystem verschiedene Schlüssel, die letztendlich von den Anmeldeinformationen des Benutzers abgeleitet und im Benutzerprofil (%AppData%\Microsoft\Protect\{SID) gespeichert werden. Zusätzlich wird noch ein computerspezifischer Schlüssel unter %Windir%\System32\Microsoft\Protect abgelegt. Dadurch ist es mit diesem Verfahren auch nicht möglich, auf zwei verschiedenen Systemen zu ver- und zu entschlüsseln.

Mehrfach ist nachgewiesen worden, dass die Sicherheit dieser Schnittstellen Löcher hat und durch verschiedene Möglichkeiten ausgehebelt werden kann (siehe z. B.  Black Hat DC 2010, DPAPIck oder DPAPI internal logic). Trotzdem ist die Sicherheit immer noch besser, als Klartext-Passworte in PowerShell-Skripten und durch die einfache Verwendung sicherlich eine sinnvolle Option.

Hinweis: Durch den Verwendung von Sysprep wird die DPAPI auf einem System neu initialisiert und vorher erstellte SecureString-Zeichenketten sind nicht mehr gültig.

Wer mehr zu DPAPI erfahren möchte sollte sich diesen Artikel durchlesen, der sehr tief in die Materie einsteigt.

Microsoft Cryptographic API (MSCAPI)

Cryptography API: Next Generation (CNG) ist Microsofts Kryptographieplattform ab Windows Vista/Windows Server 2008, und ersetzt die CryptoAPI der vorherigen Versionen von Windows. Alle deren Funktionen werden weiterhin unterstützt. Mithilfe der CNG können Kryptographieanwendungen in hochsicherheitsrelevanten Umgebungen realisiert werden.

Ausblick – Windows 10: Next Generation Credentials

Als Bestandteil der Architekturänderungen von Windows 10 werden neue Windows Dienste zum Schutz der Identität auf einem Windows System bereitgestellt: Next Generation Credentials. In der aktuellen Windows 10 Technical Preview sind diese Dienste jedoch nicht enthalten.

Quelle: Kostenloses E-Book: Introducing Windows 10 for IT-Professionals

Weitere Informationen zu diesem neuen Dienst sind bisher nur spärlich verfügbar und ich hoffe, dass sich dies zeitnah ändert. Auf meiner virtuellen Maschine ist der Dienst bereits verfügbar und aktiv. Jedoch habe ich bisher noch keine Ahnung, wie man diesen verwenden kann:

image

image

Read-Host -AsSecureString

Das Cmdlet Read-Host hat den Parameter -AsSecureString. Wenn Sie diesen Parameter verwenden, werden Sie zuerst einmal feststellen, dass während der Eingabe nicht die eingegeben Zeichen, sondern nur Sternchen erscheinen:

image

Außerdem fällt auf, dass nicht der eingegebene Text zurückgeliefert wird, sondern ein SecureString. Was sie damit alles anfangen können, werde ich Ihnen im Folgenden erläutern.

Dieses Cmdlet können Sie sehr gut dazu verwenden, Passwörter auf einem System zu speichern, bevor das eigentliche PowerShell-Skript ausgeführt wird. Es ist jedoch nicht geeignet als Bestandteil in automatisierten Skripten zu arbeiten.

Eine weitere Möglichkeit einen SecureString aus einer beliebigen Zeichenkette über die Eingabe von der Tastatur zu erstellen, besteht in der Verwendung der Methode ReadlineAsSecureString.

image

ConvertTo-SecureString

Dieses CmdLet dient zu Konvertierung einer beliebige Zeichenfolge in einen SecureString. Der anschließend in einem PowerShell-Skript verwendet werden kann. Die Konvertierung ist mit diesem CmdLet einfach, wobei ein paar Dinge beachtet werden müssen:

$pwd = "StrengGeheim"            
$pwd | ConvertTo-SecureString -AsPlainText -Force            
ConvertTo-SecureString -String $pwd -AsPlainText -Force

Um eine (Klartext-)Zeichenfolge in einen SecureString mit diesem Cmdlet zu konvertieren, können Sie die Zeichenfolge entweder als Parameter übergeben oder als Pipe weiterleiten. Wichtig sind aber die anderen beiden Parameter -AsPlainText (besagt, dass eine unverschlüsselte Zeichenfolge verschlüsselt werden soll) und der Parameter -Force macht dies erst möglich (ohne Force wird eine Fehlermeldung angezeigt, die darauf hinweist, dass der Force-Parameter fehlt). Als Ergebnis des oben dargestellten Skript-Ausschnitts erhalten Sie, wie eigentlich nicht anders zu erwarten, zwei SecureStrings:

image

Ein direkter Vergleich dieser beiden SecureStrings ist nicht direkt möglich. Wer es selbst einmal ausprobieren möchte, kann gerne dieses Skript verwenden:

$pwd = "StrengGeheim"            
$p1 = $pwd | ConvertTo-SecureString -AsPlainText -Force            
$p2 = ConvertTo-SecureString -String $pwd -AsPlainText -Force            
if ($p1 -eq $p2) {            
    Write-Host "p1 und p2 Identisch"            
}            
else {            
    Write-Host "p1 und p2 stimmen nicht überein"            
}

Außer der Konvertierung einer Klartext-Zeichenfolge kann dieses CmdLet jedoch auch dazu verwendet werden, einen bereits bestehenden SecureString (hexadezimal gespeichert), in einen für das System wieder lesbaren SecureString zu konvertieren:

[Security.SecureString]$Pwd = ConvertTo-SecureString -String "01000000d08c9ddf0115d1118c7a00c04fc297eb010000006e99a3e986c2c04f82d14d3b209dcd130000000002000000000003660000c000000010000000fbb7e17dbcac2802d2200d61d7af4b8f0000000004800000a00000001000000067c11816e2c32c211f3371f470fca5db18000000dbc1dfb8384875ddfd298cb54f0699b38c2217740f8cc9ec14000000dca9d8e3615d0850113f96dff7992b142bda2590"

Eine Entschlüsselung dieses Passworts ist nur für den Benutzer und auf dem Computer möglich, der es verschlüsselt hat. Verwendet ein anderer Benutzer diesen SecureString oder versucht der gleiche Benutzer auf einem anderem Computer die Zeichenfolge zu lesen, erhält er eine Fehlermeldung:

image

Hinweis: Leeren Zeichenketten können nicht in einen SecureString konvertiert werden.

Somit ist

[SecureString]$password =ConvertTo-SecureString -String „“ -AsPlainText –Force

nicht zulässig.

Interessant ist, dass Sie mithilfe dieses Cmdlet für immer die gleiche Zeichenkette abweichende Ergebnisse erhalten (Beginn der Unterschiede in den Ergebnissen sind mit einem roten Strich | gekennzeichnet):

image

Dieses Cmdlet enthält drei Parametersets:

  • Plaintext: ConvertTo-SecureString [-String] [-AsPlainText] [-Force]
  • Secure: ConvertTo-SecureString [-String] [[-SecureKey] ]
  • Open: ConvertTo-SecureString [-String] [-Key <byte[]>]

Diese möchte ich Ihnen im Folgenden anhand anderer Cmdlets noch genauer erklären.

Security.SecureString-Objekt

Eine andere Alternative einen SecureString zu erzeugen, ist die direkte Verwendung des Security.SecureString-Objektes. Im folgenden Beispiel erstelle ich zuerst ein SecureString-Objekt und übergebe ihm anschließend die zur Verschlüsselung vorgesehene Zeichenkette (in diesem Fall “p@ssw0rd”):

$x = new-object security.SecureString            
[char[]]"p@ssw0rd" | ForEach-Object {$x.AppendChar($PSItem)}            
 
$x.Clear()            
$x.Dispose()

Anschließend lösche ich den Inhalt des SecureString aus dem Arbeitsspeicher (Methode “Clear”) und gebe die Variable frei (Methode “Dispose”), so dass sie der Carbage Collector entfernen kann. Diese zwei Methoden sollten Sie immer verwenden, wenn Sie mit SecureString-Variablen arbeiten.

Hinweis: Aus Vereinfachungsgründen und zur besseren Darstellung habe ich diese beiden Methoden in diesem Artikel nicht konsequent verwendet. Sollten Sie eine solche Lösung für Ihr Skript planen, empfehle ich Ihnen diese Methoden zum Bereinigen des Arbeitsspeichers nicht zu vergessen.

ConvertFrom-SecureString

Jetzt werden Sie sich sicherlich fragen, wie Sie den Passwort-Klartext in den Secure-Text transformieren können, den Sie anschließend in Ihr PowerShell-Skript integrieren können. Dazu stellt Ihnen die PowerShell das CmdLet ConvertFrom-SecureString zur Verfügung:

ConvertTo-SecureString -AsPlainText "P@ssw0rd" -Force | ConvertFrom-SecureString

image

Nachdem Sie die Passwort-Zeichenkette (P@ssw0rd) durch Ihr Password ersetzt haben, sollten Sie die Zeile unter den Benutzer-Credentials und auf dem Computer ausführen, der auch später das PowerShell-Skript ausführen wird.

Danach können Sie in Ihrem PowerShell-Skript das Passwort durch den SecureString ersetzen. Dies erfolgt in drei Schritten:

  1. Kopieren Sie das Passwort aus Ihrem PowerShell-Skript (Zeile 3) in die Zeile 6 und führen diese Zeile aus.
  2. Das Ergebnis dieser Zeile fügen Sie dann in Zeile 11 ein und
  3. löschen Zeile 3 bis 10
# Skript alt            
$username = "Sylvio"            
$pwd = "P@ssw0rd"            
            
# Passwort-Konvertierung zu verschlüsselter Zeichenfolge            
ConvertTo-SecureString -AsPlainText "P@ssw0rd" -Force | 
    ConvertFrom-SecureString                        
            
# neues Skript            
[Security.SecureString]$Pwd = ConvertTo-SecureString -String `
    "01000000d08c9ddf0115d1118c7a00c04fc297eb010000009045f4095807ef42” + `
    "970646542972fd3b0000000002000000000003660000c0000000100000003bc8" + `
    "6480e2839c58d7b6cfc28096b2a00000000004800000a000000010000000edd2" + `
    "564c5ddcdc1a02ea51455d086d38180000002aefad1751612cda36e9a565450b" + `
    "dc43b8bf7c704a0e0e8d14000000ab8d8317b43718c698818b53ecdae79d46b1" + `
    "fc06"
SecureString speichern

Im folgenden, sehr einfachen Beispiel geht es darum, einen SecureString auf einem Datenträger zu speichern (erster Block) und diesen wieder einzulesen (letzter Block). Um Ihnen zu zeigen, wie die Datei aussieht, habe ich in der Mitte noch eine Zeile eingefügt, die aber nur zur Darstellung dessen dient, was sie abgespeichert haben.

[string]$Text = "Das ist sehr geheim"            
ConvertTo-SecureString -String $Text -AsPlainText -Force |             
    ConvertFrom-SecureString |              
    Export-Clixml -Path "C:\temp\pwd1.xml"            
            
Get-Content C:\temp\pwd1.xml            
            
$File1 = Import-Clixml -path "C:\temp\pwd1.xml"            
ConvertTo-SecureString -string $File1

Wo Sie die Datei mit dem SecureString ablegen, spielt in diesem Szenario eigentlich keine Rolle, denn er ist ja verschlüsselt. Somit können Sie die Datei auf einem USB-Stick, auf einem Fileshare oder in der Cloud speichern. Doch auch hier sollten Sie beachten: Keine Verschlüsselung ist unknackbar!

Das Zurückkonvertieren in den SecureString kann jedoch nur auf dem Computer und unter dem Benutzer erfolgen, unter dem er auch erstell wurde.

SecureString in Klartext konvertieren

Nachdem ich Ihnen in den vorangegangenen Abschnitten erklärt habe, wie Sie eine Zeichenkette als SecureString konvertieren können, fehlt nun natürlich noch der Weg, wie Sie einen SecureString wieder in einen Klartext umwandelt können. Dazu gibt es in der aktuellen PowerShell noch kein entsprechendes Cmdlet, so dass Sie hierfür auf Klassen von .NET Framework zurückgreifen müssen:

[Security.SecureString]$Pwd = ConvertTo-SecureString -String „01000000d08c9ddf0115d1118c7a00c04fc297eb010000009045f4095807ef42970646542972fd3b0000000002000000000003660000c0000000100000003bc86480e2839c58d7b6cfc28096b2a00000000004800000a000000010000000edd2564c5ddcdc1a02ea51455d086d38180000002aefad1751612cda36e9a565450bdc43b8bf7c704a0e0e8d14000000ab8d8317b43718c698818b53ecdae79d46b1fc06“ $BSTR = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($pwd) [System.Runtime.InteropServices.Marshal]::PtrToStringAuto($BSTR) [System.Runtime.InteropServices.Marshal]::ZeroFreeBSTR($BSTR)

$pwd.Clear()
$pwd.Dispose()

Remove-Variable pwd

Kurze Erklärung zu dem vorangegangenen Skript-Beispiel:

  1. Zeile: Konvertieren eines Klartextes (P@ssw0rd) in einen SecureString
  2. Zeile: Initialisierung der Konvertierung von SecureString in eine Zeichenkette
  3. Zeile: Ausgabe des Passworts in Klartext
  4. Zeile: Bereinigen des Arbeitsspeichers (Löschen des Klartextkennworts im Speicher)
  5. Zeile bis Ende: Entfernen der $pwd-Variablen aus dem Arbeitsspeicher

Hinweis: Schreiben Sie den in Klartext konvertierten SecureString in eine Variable, so befindet sich diese im Arbeitsspeicher und kann gelesen werden. Somit sind Sie selbst dafür verantwortlich, die Variable zu bereinigen (Löschen und/oder Überschreiben).

Dateiverschlüsselung

Nachdem ich bereits im ersten Teil dieses Artikels verschiedene Tools vorgestellt habe, mit denen Sie Dateien verschlüsseln können, möchte ich Ihnen in diesem Abschnitt Möglichkeiten vorstellen, wie Sie mit einem eigenen PowerShell-Skripten andere Dateien (wie z. B. auch PowerShell-Skripte) wirkungsvoll schützen können.

Auch wenn es in den vorhergehenden Abschnitten vorrangig um den Schutz von Passwörtern ging, können Sie diese Verfahren auch zum Schutz ganzer Dateien (wie z. B. PowerShell-Skripte) verwenden. Ein solches Script können Sie recht einfach selbst erstellen:

@‘ Write-Host „Nachricht generiert um “ $([System.DateTime]::Now.ToString(„dd. MMMM yyyy u\m hh:mm \U\h\r“)) ‚@ | Out-File -FilePath „C:\TEMP\SimpleMessage.ps1“ [String]$s = Get-Content -Path „C:\TEMP\SimpleMessage.ps1“ $sEncrypted = ConvertTo-SecureString -String $s -AsPlainText -Force | ConvertFrom-SecureString $sEncrypted | Out-File -FilePath „C:\temp\SimpleMessage.ps2“ [String]$s = Get-Content -Path „C:\TEMP\SimpleMessage.ps2“ $sDecrypted = ConvertTo-SecureString -String $s $BSTR = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($sDecrypted)

Invoke-Expression -Command $([System.Runtime.InteropServices.Marshal]::PtrToStringAuto($BSTR))

$encoded = [System.Convert]::ToBase64String([System.Text.Encoding]::Unicode.GetBytes(` [System.Runtime.InteropServices.Marshal]::PtrToStringAuto($BSTR))) powershell.exe -encodedCommand $encoded [System.Runtime.InteropServices.Marshal]::ZeroFreeBSTR($BSTR) $s=„01000000d08c9ddf0115d1118c7a00c04fc297eb010000009045f4095807ef42970646542972fd3b0000000002000000000003660000c000000010000000fa693e427ed81ad80f198e5511e292730000000004800000a0000000100000006810e259216686a7bb21fc48917a5cc7d8000000f1b1ad415b7bde1b7c30d21194df53f717d0dc2ac6a1256d8a4bbeb240574bd9651da9389d73fc53bf0b7e456ec0f3cac701de4be7937b5cd1386bf12953eba925555009c6853e5d98bc4da6a070431ace0ea91c33fe48a15b9f15964e920c7b6e7e34f0e48a493d5fbc3cf2da5464a5c2cd6dae4ec8ebb6a82661a01d60265ee1a06b883f0b61c5eab0a5738e55e675a6ee7ae6c0fdac8c2d2a85aa4dc7939b8f56dba127556a56a170bf0cb510fc771981fe974b06a1e81b1942001b49f82f5ca340a2794f84e7292fdb952985f423e995fbd4dc0746b614000000e9c1dda6b8c5a5b21d604c9ce5e2abc8b5532d10“ $sDecrypted = ConvertTo-SecureString -String $s $BSTR = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($sDecrypted) $encoded = [System.Convert]::ToBase64String([System.Text.Encoding]::Unicode.GetBytes(` [System.Runtime.InteropServices.Marshal]::PtrToStringAuto($BSTR))) powershell.exe -encodedCommand $encoded [System.Runtime.InteropServices.Marshal]::ZeroFreeBSTR($BSTR)

Hinweis: Die in dem vorangegangenen Skript als Variable $s angegebene Zeichenfolge wurde auf meinem PC erstellt und ist auf anderen Systemen nicht anwendbar. Für Ihr System sollten Sie diese Zeichenkette, durch die in der Datei C:\TEMP\SimpleMessage.ps2 erstellten Zeichenkette ersetzen.

Kurze Beschreibung des vorangegangenen Beispiels:

  • im ersten Abschnitt wird ein PowerShell-Skript (als Beispiel erstellt) und als Datei auf Festplatte geschrieben
  • im zweiten Abschnitt wird das PowerShell-Skript von Festplatte gelesen und verschlüsselt als neue Datei (*.ps2) wieder gespeichert.
  • im dritten Abschnitt wird die verschlüsselte Datei eingelesen, entschlüsselt und mit Invoke-Expression ausgeführt
  • im vierten Abschnitt wird das entschlüsselte Skript base64-kodiert und mit PowerShell.Exe ausgeführt
  • im fünften Abschnitt wurde lediglich der Inhalt der Datei direkt in eine Variable geschrieben, entschlüsselt, das Ergebnis base64-kodiert und ausgeführt .

Wichtig: Das vorangegangene Beispiel soll nur die prinzipielle Funktionsweise dieser Verschlüsselungstechnik zeigen und deshalb habe ich Ver- und Entschlüsselung in ein Skript zusammengestellt. In der Praxis macht es natürlich nur Sinn, wenn die Ver- und Entschlüsselung voneinander getrennt sind.

Die Entschlüsselung kann nur durch den gleiche Benutzer auf dem gleichen Computer vorgenommen haben, der das Skript auch verschlüsselt hat (siehe Beschreibung zu SecureString).

Hinweis: Auf eine interessante Möglichkeit zur Verschlüsselung eines PowerShell-Skripts bin ich auf folgenden Link gestoßen:

https://github.com/mattifestation/PowerSploit/blob/master/ScriptModification/Out-EncryptedScript.ps1

Das Interessante an dieser Lösung ist, dass Sie trotz sehr mächtiger Verschlüsselung das Skript nach Eingabe des Passworts weiter verwenden können.

Out-EncryptedScript -ScriptPath "C:\TEmp\SimpleMessage.ps1" -Password password -Salt salty -FilePath "c:\temp\SimpleMessage2.ps1"             
            
            
[String] $cmd = Get-Content "c:\temp\SimpleMessage2.ps1"             
Invoke-Expression $cmd            
$decrypted = de password salty            
Invoke-Expression $decrypted

image

Das Skript selbst ist jedoch unlesbar:

image

Advanced Encryption Standard

Der Advanced Encryption Standard (AES) ist ein Standard zur Verschlüsselung von Daten, der auch von Windows unterstützt wird. Er gilt als so sicher, dass dieser sogar in den USA zur Verschlüsselung von Dokumenten mit höchster Geheimhaltungsstufe verwendet wird.

Eine spezielle Integration in PowerShell gibt es bis zur aktuellen Version noch nicht, so dass Sie direkt auf die API zugreifen müssen. Die Verwendung der API stellt Ihnen jedoch erweiterte Möglichkeiten bereit. Die ich Ihnen im Folgenden an einem Code-Beispiel ausführlicher erläutern möchte.

Im ersten Schritt (Initialisierung) müssen Sie einen Encryptor definieren, der aus einem Schlüssel (Key), einen Initialisierungsvektor (IV) und Padding besteht.

$AES = New-Object System.Security.Cryptography.AesManaged            
$AES.Key = [byte[]][char[]]"orco3Y9LeCR6YYCwGC3yQifAjr05Nv8C"            
$AES.IV = [byte[]][char[]]"abcdeFghiklm#opq"            
$AES.Padding = "1#2#3.4#"            
$Encryptor = $AES.CreateEncryptor()

In einem zweiten Schritt erfolgt die Verschlüsselung der Daten in einem geschützen Arbeitsspeicherbereich.

$MemoryStream = New-Object -TypeName System.IO.MemoryStream            
$StreamMode =[System.Security.Cryptography.CryptoStreamMode]::Write            
$CryptoStream = New-Object -TypeName System.Security.Cryptography.CryptoStream `
    -ArgumentList $MemoryStream,$Encryptor,$StreamMode            
            
$InputString = "StrenGeheim§34w6"            
$InputBytes = [Text.Encoding]::Unicode.GetBytes($InputString)            
            
$CryptoStream.Write($InputBytes, 0, $InputBytes.Length)            
$CryptoStream.Dispose()            
[byte[]] $EncryptedBytes = $MemoryStream.ToArray()            
$MemoryStream.Dispose()

Im ersten Code-Block des vorangegangenen Beispiels reserviere ich einen Bereich im Arbeitsspeicher, dem ich mitteile, dass ich dort etwas verschlüsselt ablegen möchte. Der zweite Code-Block ($InputString) ist dann das, was ich eigentlich verschlüsseln möchte und im dritten Code-Block lasse ich mir dann das verschlüsselte Byte-Array zurückgeben und bereinige den Speicher. Damit ist die Verschlüsselung abgeschlossen und wir können uns die Entschlüsselung ansehen:

$AES = New-Object System.Security.Cryptography.AesManaged            
$AES.Key = [byte[]][char[]]"orco3Y9LeCR6YYCwGC3yQifAjr05Nv8C"            
$AES.IV = [byte[]][char[]]"abcdeFghiklm#opq"            
$AES.Padding = "1#2#3.4#"            
$Decryptor = $AES.CreateDecryptor()            
            
$MemoryStream = New-Object -TypeName System.IO.MemoryStream            
$StreamMode =[System.Security.Cryptography.CryptoStreamMode]::Write            
$CryptoStream = New-Object -TypeName System.Security.Cryptography.CryptoStream `
    -ArgumentList $MemoryStream,$Decryptor,$StreamMode            
$CryptoStream.Write($EncryptedBytes, 0,            
$EncryptedBytes.Length)            
$CryptoStream.Dispose()            
[byte[]] $PlainBytes = $MemoryStream.ToArray()            
$MemoryStream.Dispose()            
“Output Data = ” + [Text.Encoding]::Unicode.GetString($PlainBytes)

Diese besteht im ersten Bereich aus einem Decrypter mit den vorher verwendeten Schlüssel, Initialisierungsvektor und dem Padding. Anschließend lande ich die verschlüsselte Zeichenkette (Byte Array) wieder in den geschützten Arbeitsspeicherbereich und lasse sie dort entschlüsseln. Das Ergebnis konvertiere ich wieder in eine lesbare Zeichenkette und wenn Sie nichts falsch gemacht haben, sollte nun am Ende

image

und somit wieder die am Anfang zur Verschlüsselung übergebene Zeichenkette stehen.

Das Beispiel soll Ihnen zeigen, dass auch mit der API die Ver- und Entschlüsselung von Zeichenketten eigentlich unproblematisch ist und speziell in Bereichen, in denen hohe Sicherheit gefordert ist, auch eingesetzt werden sollte.

Um die Sicherheit noch weiter zu verbessern, können Sie zusätzlich z. B. noch Hash oder Salt anwenden. Doch das würde für diesen kurzen Artikel wohl zu weit führen. Doch bestimmt bietet Ihnen das Internet auch dazu weitere Informationen.

Rijndael-Algorithmus

Eine Sonderform von AES ist der Rijndael-Algorithmus, der mehrere Verbesserungen gegenüber dem ursprünglichen Standard aufweist. Auch dieser Algorithmus ist bereits in der Security-API von Windows .NET Framework ab 2.0 in eigenen Klassen des Namespaces System.Security.Cryptography verewigt worden (https://msdn.microsoft.com/de-de/library/system.security.cryptography.rijndael(v=vs.110).aspx und https://msdn.microsoft.com/en-us/library/system.security.cryptography.rijndaelmanaged(v=vs.110).aspx).

Auch in das PowerShell-Cmdlet ConverFrom-SecureString hat dieser Standard Einzug gehalten und lässt sich ohne großen zusätzlichen Aufwand verwenden, indem der zusätzliche Parameter “Key” verwendet wird, um einen individuellen Schlüssel festzulegen. Hier ein Beispiel:

ConvertTo-SecureString -AsPlainText "P@ssw0rd"Force | 
    ConvertFrom-SecureString -Key $([byte[]][char[]]"StrenGeheim§34w6")

Die Schlüssellänge ist für AES auf 128, 192 bzw. 256 bit (8bit = 1 Byte somit 16, 24 oder 32 Byte für AES) festgelegt. Weicht die Schlüssellänge von den vorgegebenen Werten ab, so werden Sie durch das Cmdlet freundlich darauf hingewiesen.

image

Damit Sie sich nicht für jeden Anwendungsfall einen Schlüssel ausdenken müssen, gibt es wiederum eine API, die Ihnen einen entsprechenden dynamisch Schlüssel erstellt (in dem Beispiel wird ein Schlüssel mit einer Länge von 32 Zeichen erstellt):

$Key = New-Object Byte[] 32               
[Security.Cryptography.RNGCryptoServiceProvider]::Create().GetBytes($Key)
([char[]]$Key) -join ""

Durch die Verwendung des Schlüssel-Parameters bei diesem Cmdlet haben sie auch keine Bindung mehr an einen Benutzer, sondern können diesen SecureString Benutzer- und Computer-unabhängig verwenden. Nachteil: Sie haben nun einen Schlüssel, den sie wiederum irgendwo ablegen bzw. während der Verarbeitung des Skripts eingeben müssen.

Hinweis: Wenn Sie den Key-Parameter nicht verwenden, nutzt das PowerShell-Cmdlet die Windows Data Protection API (DPAPI) zum Ver- und Entschlüsseln von Zeichenfolgen.

Credentials

Da ein Passwort selten allein verwendet wird, sondern oft an einem Benutzernamen gebunden ist, gibt es das CmdLet Get-Credential, mit dem Sie zwei beliebige Zeichenfolgen eingeben können:

image

Der Benutzername wird dabei als Username im Klartext zurückgegeben und das Passwort als SecureString. Um Ihnen die Eigenschaften dieses Credential-Objektes zu zeigen, habe ich diese einerseits unter Verwendung des CmdLets ConvertFrom-SecureString in eine darstellbare Form transformiert und anschließend alle Eigenschaften dieses Objekts als Liste ausgegeben:

$cred = get-credential -Message "Bitte geben Sie Benutzername und Kennwort ein" -UserName "Sylvio"            
$cred | Add-Member -MemberType NoteProperty -Name 'PwdSecureString' -Value $($cred.password | convertFrom-SecureString)            
$cred | Select * | fl

Das Ergebnis sieht dann wie folgt aus (PwdSecureString-Property aus Platzgründen in der Anzeige gekürzt):

image

Hinweis: Um die Authentifizierungsaufforderung in der Befehlszeile anzuzeigen, fügen Sie den ConsolePrompting-Registrierungseintrag (HKLM:\SOFTWARE\Microsoft\PowerShell\1\ShellIds\ConsolePrompting) ein, und legen Sie dessen Wert auf „true“ fest. Wenn der ConsolePrompting-Registrierungseintrag nicht vorhanden ist oder wenn der Wert „False“ ist, wird die Authentifizierungsaufforderung in einem Dialogfeld angezeigt.

image

Als Alternative zu Get-Credential können Sie auch folgende Methode PromptForCredential der Powershell-UI verwenden:

$Credential = $host.ui.PromptForCredential(`
    "Benutzeranmeldung",`
    "Bitte geben Sie Benutzername and Passwort ein.", `
    "", `
    "NetBiosUserName")

Interessanterweise greift auf diesem Weg aber der Registry-Eintrag zur Unterdrückung der Anzeige des Dialogs für die Benutzer-/Kennwort-Eingabe nicht.

Auch wenn der vorher gezeigte Dialog oder die Eingabe so aussieht, als ob ein Passwort z. B. gegen eine Windows Domäne überprüft wird, ist dies definitiv nicht der Fall. Sollten Sie dies benötigen, müssen Sie sich selbst darum kümmern. Dazu hier ein Code-Beispiel:

$DomainUser = "Domain\Username"            
$PasswordText = "StrengGeheim"            
Add-Type -AssemblyName System.DirectoryServices.AccountManagement            
$ct =[System.DirectoryServices.AccountManagement.ContextType]::Domain            
$pc = New-Object System.DirectoryServices.AccountManagement.PrincipalContext $ct,$($DomainUser.substring(0,$DomainUser.indexof("\")))            
if (!($pc.ValidateCredentials(            
        $DomainUser.substring($DomainUser.indexof("\")+1),            
        $PasswordText, [DirectoryServices.AccountManagement.ContextOptions]::Negotiate            
    ))) {            
    Write-Error "Credentials for user '$DomainUser' are invalid"            
    Exit            
}
PSCredential-Klasse

Die Objektklasse PSCredential, die z. B. das CmdLet Get-Credential als Rückgabewert liefert, kann direkt in Ihrem PowerShell-Skript verwenden:

[String]$username=”Sylvio”            
[SecureString]$password =ConvertTo-SecureString -String "P@ssw0rd" -AsPlainText -Force            
$cred = New-Object -TypeName System.Management.Automation.PSCredential -argumentlist $username,$password

Credentials lassen sich sehr vielseitig in PowerShell verwenden, da diese durch viele Cmdlets unterstützt werden. Wenn Sie wissen wollen, welche Cmdlets Parameter vom Type Credential verwenden, können Sie folgenden Befehl ausführen:

Get-Help * -Parameter Credential

Als Beispiel habe ich mir das Cmdlet “Get-WmiObject” ausgewählt, dem Sie ein Credential-Objekt als Parameter übergeben können:

$argList = @{            
    Username = "domain\username";            
    password = ConvertTo-SecureString -String "******" -AsPlainText -force            
}            
$cred = New-Object -TypeName "System.Management.Automation.PSCredential" -ArgumentList $argList            
             
Get-WmiObject -ComputerName "myserver" -Credential $cred -Class "Win32_Bios"

Um das Passwort aus den eingegebenen Credentials wieder in eine lesbare Zeichenkette/Originalpasswort zu konvertieren, können Sie folgende Eigenschaften des PSCredential-Objekts verwenden:

$cred = Get-Credential            
$cred.GetNetworkCredential().password
Data Protection API

Die Data Protection API (DPAPI) stellt im Namespace Security.Cryptography.ProtectedData zwei Methode zum Ver- (Protect) und Entschlüsseln (Unprotect) zur Verfügung. Mit dieser Klasse ist es sehr einfach möglich, im maschinen- oder benutzerspezifischen Kontext Zeichenfolgen zu ver-/entschlüsseln. Im folgenden Beispiel wird eine Zeichenkette im Scope des Computers ver- und entschlüsselt.

$stringBytes = [Text.Encoding]::UTF8.GetBytes("Das ist geheim")            
$encryptedBytes = [Security.Cryptography.ProtectedData]::Protect( $stringBytes, $null, [Security.Cryptography.DataProtectionScope]::LocalMachine )            
            
$decryptedBytes = [Security.Cryptography.ProtectedData]::Unprotect( $encryptedBytes, $null, [Security.Cryptography.DataProtectionScope]::LocalMachine )            
[Text.Encoding]::UTF8.GetString( $decryptedBytes )

Um die Sicherheit noch weiter zu erhöhen, kann zusätzlich noch ein Kennwort angegeben werden. Dazu müssen Sie den zweiten Parameter (im obigen Beispiel mit $null vorbesetzt) durch ein Byte-Array mit dem Kennwort z. B. [Text.Encoding]::UTF8.GetBytes(P@ssw0rd) ersetzen.

RSA

In der modernen Kryptologie hat sich die RSA-Verschlüsslung (asymmetrisches Verschlüsselungsverfahren) als eine der sichersten Methoden durchgesetzt. Sie wird heute in wichtigen Bereichen wie im Bankenwesen (z.B. bei der Verschlüsslung von Geheimzahlen), Mobilfunknetzen oder bei Geheimdiensten verwendet. Trotzdem hat auch dieses Verfahren seine Schwachstellen (wie z. B. die Schlüssellänge oder Fehler in der Implementierung).

Wer den mathematischen Hintergrund dieses Verfahren genauer ansehen möchte, dem kann ich folgende Seite empfehlen: http://www.mathematik.de/ger/information/wasistmathematik/rsa/rsa_self.html

Das folgende Skript dient zur zum Ver-/Entschlüsseln einer Zeichenfolge. Die Besonderheit dieses Skriptes ist, dass beim Verschlüsseln der PublicKey und zum Entschlüsseln der PrivateKey verwendet wird:

function Encrypt-RSA {            
    param (            
        [String]$Text,            
        [Security.Cryptography.RSAParameters]$RSAKey            
    )            
    [Byte[]]$TextBytes = [Text.Encoding]::UTF8.GetBytes($Text)            
    $rsa = New-Object Security.Cryptography.RSACryptoServiceProvider            
    $rsa.ImportParameters($RSAKey);            
    [byte[]]$encryptedData=$rsa.Encrypt($TextBytes, $false)            
    return [Convert]::ToBase64String($encryptedData)            
}            
             
function Decrypt-RSA {            
    param (            
        [String]$Text,            
        [Security.Cryptography.RSAParameters]$RSAKey            
    )            
    [Byte[]]$TextBytes = [Convert]::FromBase64String($Text)            
    $rsa = New-Object Security.Cryptography.RSACryptoServiceProvider            
    $rsa.ImportParameters($RSAKey);            
    [byte[]]$decryptedData=$rsa.Decrypt($TextBytes, $false)            
    return [Text.Encoding]::UTF8.GetString($decryptedData)            
}            
             
$rsa = New-Object Security.Cryptography.RSACryptoServiceProvider            
$a = Encrypt-RSA -Text "StrengGeheim" -RSAKey $rsa.ExportParameters($false)            
Decrypt-RSA -Text $a -RSAKey $rsa.ExportParameters($true)

In dem vorigen Beispiel habe ich aus Vereinfachungsgründen auf Schlüsselaustausch,  Validierung des öffentlichen Schlüssels, Erzeugen eines Hash-Wertes und Signieren der Nachricht verzichtet. Zur Verbesserung der Sicherheit sollte Sie jedoch zusätzlich über die Einführung solcher Maßnahmen nachdenken. Wenn Sie Fragen zur Implementierung haben, dürfen Sie mich auch gerne ansprechen.

Zertifikate

Anstatt mit einem RSA-Schlüsselpaar zu arbeiten, können Sie stattdessen auch ein Zertifikat z. B. aus dem Zertifikat-Store verwenden. Das folgende einfache Beispiel soll Ihnen die Verwendung eines Zertifikates für die Verschlüsselung demonstrieren:

function Encrypt-RSACertificate {            
    param (            
        [String]$Text,            
        [String]$CertificatePath            
    )            
            
    $stringBytes = [Text.Encoding]::UTF8.GetBytes( $Text )            
    $Certificate = Get-Item -Path $CertificatePath            
    if (!$Certificate) {            
        return            
    }            
    $key = $Certificate.PublicKey.Key            
    if( $key -isnot ([Security.Cryptography.RSACryptoServiceProvider])) {            
        Write-Error ('Certificate ''{0}'' (''{1}'') is not an RSA key. Found a public key of type ''{2}'', but expected type ''{3}''.' -f $Certificate.Subject,$Certificate.Thumbprint,$key.GetType().FullName,[Security.Cryptography.RSACryptoServiceProvider].FullName)            
        return            
    }            
            
    try {            
        $encryptedBytes = $key.Encrypt( $stringBytes, $null )            
    }            
    catch {            
        Write-Error -Exception $_.Exception            
        return            
    }            
            
    return [Convert]::ToBase64String( $encryptedBytes )            
}

Im vorangegangenen Code-Block erfolgt die Verschlüsselung einer beliebigen Zeichenfolge anhand des öffentlichen Schlüssels eines Zertifikats.

function Decrypt-RSACertificate {            
    param (            
        [String]$Base64,            
        [String]$CertificatePath            
    )            
            
    $stringBytes = [Convert]::FromBase64String($Base64)            
    $Certificate = Get-Item -Path $CertificatePath            
    if (!$Certificate) {            
        return            
    }            
    $key = $Certificate.Privatekey            
    if( $key -isnot ([Security.Cryptography.RSACryptoServiceProvider])) {            
        Write-Error ('Certificate ''{0}'' (''{1}'') is not an RSA key. Found a public key of type ''{2}'', but expected type ''{3}''.' -f $Certificate.Subject,$Certificate.Thumbprint,$key.GetType().FullName,[Security.Cryptography.RSACryptoServiceProvider].FullName)            
        return            
    }            
            
    try {            
        $decryptedBytes = $key.Decrypt( $stringBytes, $false )            
    }            
    catch {            
        Write-Error -Exception $_.Exception            
        return            
    }            
            
    return [Text.Encoding]::UTF8.GetString( $decryptedBytes )            
}

Die Entschlüsselung erfolgt mit dem privaten Schlüssel (private Key). Ansonsten ist der Aufbau der Entschlüsselungsfunktion sehr ähnlich der Verschlüsselungsfunktion.

Zur Durchführen der Ver- und Entschlüsselung habe ich folgendes Zertifikat aus meinem Store verwendet:

$certificate = "Cert:\CurrentUser\My\6CF4EAC6A6B468C9B62E74F09CC0433C0EC77161"            
$a = Encrypt-RSACertificate -Text "Das ist geheim" -CertificatePath $certificate            
Decrypt-RSACertificate -Base64 $a -CertificatePath $certificate

Auch in diesem Beispiel wurde auf Beiwerk zur Verbesserung der Sicherheit verzichtet, damit einfacher zu verstehen ist, wie das Verfahren funktioniert.

Verwaltung eigener Anmeldeinformationen

In Windows 7 und späteren Versionen hat Microsoft ein Tool zur Verwaltung eigener Anmeldeinformationen (Credential Manager oder Windows Vault) integriert. Sie finden diese Komponente als Teil der Systemsteuerung

image

image

und können mit dem Kommandozeilentool cmdkey darauf zugreifen, um z. B. die gespeicherten Credentials abzufragen:

cmdkey /list

Doch auch per PowerShell ist ein direkter Zugriff (ab Windows 8) auf die gespeicherten Anmeldeinformationen möglich. Das folgende Skript-Beispiel besteht aus drei Code-Blöcken:

  1. Abfrage der gespeicherten Credentials im  Windows Vault
  2. Anlegen neuer Credentials im Windows Vault
  3. Abfragen eines spezifischen Credential-Objekts und Konvertierung in PSCredentials
# Get All            
[void][Windows.Security.Credentials.PasswordVault,Windows.Security.Credentials,Contenttype=WindowsRuntime]            
$vault = new-object Windows.Security.Credentials.PasswordVault -ErrorAction silentlycontinue            
$vault.RetrieveAll() |             
    ForEach-Object {            
        $PSitem.RetrievePassword()            
        $PSitem            
    }            
            
# Create New            
$ResourceName = "http:\\intranet.de"            
$cred = Get-Credential            
$WinCred = New-Object Windows.Security.Credentials.PasswordCredential -ArgumentList ($ResourceName, $cred.UserName, $cred.GetNetworkCredential().Password)             
$vault.Add($winCred)            
            
# Retrieve one and convert to PSCredential            
$winCred = $vault.Retrieve($ResourceName, $cred.UserName)            
New-Object System.Management.Automation.PSCredential -ArgumentList ($winCred.UserName, (ConvertTo-SecureString $winCred.Password -AsPlainText -Force))            

Hinweis: Dem interessierten Leser wird aufgefallen sein, dass ich am Beginn des Skripts eine für PowerShell sehr ungewöhnliche Zeile eingefügt habe, die ich noch näher erklären möchte:

[void][Windows.Security.Credentials.PasswordVault,Windows.Security.Credentials, Contenttype=WindowsRuntime]

Mit Windows 8 hat Microsoft neben der Kacheloberfläche auch ein neues Betriebssystem Windows RT (für Windows Mobile Devices auf ARM Architektur) eingeführt. Teile dieses Betriebssystems haben auch Einzug in das “normale” Desktopbetriebssystem gehalten. Zu sehe sind diese Betriebssystemkomponenten immer dann, wenn Sie eine Windows-Store-App (z. B. Windows Mail) starten. Wie im Windows-Umfeld üblich, gibt es auch eine API für Windows RT (oder wie die offizielle Bezeichnung lautet: Windows-API-Referenz für Windows-Runtime-Apps). Was mir bisher nicht bekannt war ist, dass man diese API auch von “normalen” Windows-Anwendungen nutzen kann und somit irgendwie auch von PowerShell aus (wie dieses Beispiel zeigt).

Insofern ich Zeit finde, werde ich ggf. diesem Thema mal einen eigenen Artikel widmen, da es nicht nur spannend ist, sondern auch eine Menge Stolpersteine bereithält, wie ich bereits erfahren musste.

Das Thema mit dem Zugriff auf Windows Vault ist hochinteressant und bestimmt lässt sich dazu noch viel mehr erzählen. Doch wer sich ausführlicher mit diesem Thema beschäftigen möchte, sollte sich die folgenden Links ansehen:

Azure Key Vault

Vor Kurzem hat Microsoft einen Dienst zur Verwaltung von Credentials in der Cloud als kostenpflichtige Preview zur Verfügung gestellt – Azure Key Vault. Zur Verwendung in PowerShell gibt es ein eigenes Modul, das alle wichtigen Funktionen für den Zugriff auf diesen Dienst anbietet. Zusätzlich gibt es hier verschiedene Beispiele für die Verwendung dieses Schlüsselsafe-Dienstes.

Da für viele Firmen eine Speicherung nicht öffentlichen Zeichenketten in einem Cloud-Dienst nicht in Frage kommt und ich mit diesem Dienst bisher auch noch keine weiteren Erfahrungen gesammelt habe, möchte ich dieses Kapitel an dieser Stelle beenden. Für bestimmte Anwendungsfälle mag dieser Dienst jedoch trotzdem interessant sein.

SQL Server

SQL Server bietet ebenfalls die Möglichkeit Zeichenfolgen geschützt (in einer Datenbank) zu speichern. Dazu können zwei unterschiedliche Verfahren verwendet werden:

Beide Verfahren können von PowerShell aus verwendet werden und bieten einen hohen Schutz. Beispiele für die Verwendung dieser Möglichkeiten finden Sie bei den vorangegangen angegebenen Links aus Microsoft Books Online, auf anderen Seiten im Internet oder in der Literatur:

Jedoch ist dazu ein SQL Server erforderlich und da ich meine Lösung (ausnahmsweise) ohne SQL Server implementieren möchte, möchte ich das Thema an dieser Stelle auch schon beenden (auch wenn es dazu noch eine Menge zu schreiben gäbe).

Neben der Verschlüsselung von Daten bietet SQL Server jedoch auch die Möglichkeit mit Hash zu arbeiten. Ein Beispiel dazu finden Sie unter dem Link.

Active Directory/Azure Rights Management Service

Active Directory-Rechteverwaltungsdienste (Active Directory Rights Management Services, AD RMS) ist ein Sicherheitstool in Microsoft Windows bzw. Microsoft Azure Right Management (Azure RMS) ist ein Dienst in der Cloud, die einen persistenten Datenschutz mit Datenzugriffsrichtlinien bieten. Das Verfahren wird auch als Digitale Rechteverwaltung (digital right management, DRM) bezeichnet. Für Dokumente, die mit einem RMS geschützt werden,  ist die Anwendung von Richtlinien notwendig. Dazu müssen Empfänger und Ersteller von Dokumenten eine Verbindung mit der RMS-Infrastruktur haben, um die notwendigen Daten zu erhalten. Wie der Name bereits vermuten lässt, lassen sich mit diesen Diensten Rechte auf Dateien in einem Unternehmensnetzwerk verwalten.

Da ich selbst bisher noch keine Erfahrungen mit AD RMS bzw. Azure RMS gesammelt habe, kann ich dazu auch weiter nichts berichten. Wer sich mit dem Thema auseinandersetzen möchte, sollte sich jedoch tiefergehend damit beschäftigt.

Hinweis: Die Active Directory-Rechteverwaltungsdienste (AD RMS) stehen seit vielen Jahren als lokale Lösung für Kunden zur Verfügung, um Office-Dokumente zu schützen. Microsoft Azure Rights Management ist eine neue cloudbasierte Lösung, mit der das gleiche Maß an Schutz für Kunden von Office 365 bereitgestellt wird. Quelle: http://products.office.com/de-de/business/microsoft-azure-rights-management

Für den Zugriff auf RMS steht ein API zur Verfügung, mit der man diesen Service in eigene Anwendungen integrieren kann.

Dateiebene

Wenn wirklich keine andere Möglichkeit besteht, als das Passwort in ein PowerShell-Skript zu integrieren, so sollten Sie das PowerShell-Skript wenigstens auf Dateiebene so sichern, dass niemand außer Ihnen Zugriff darauf erhält. Dazu gibt es zwei verschiedene Möglichkeiten:

  • NTFS-Berechtigungen
  • Dateiverschlüsselung
NTFS-Berechtigungen

Auf eine Datei lassen sich in Windows verschiedene Berechtigungen z. B. über den Windows Explorer, die Kommandozeile (CACLS) oder mit PowerShell festlegen. Durch diese Einstellung können Sie definieren, wer eine Datei oder Verzeichnis z. B. Lesen oder Ändern darf. Berechtigungen können dabei für einzelne Benutzer oder Benutzergruppen gesetzt werden.

image

Dieser Schutz ist ausreichend um ein Skript oder eine Passwort-Datei vor dem Zugriff durch andere Benutzer zu sichern. Administratoren können jedoch jederzeit (auch ohne explizite Berechtigung) den Besitz einer Datei übernehmen und somit auch die Datei einsehen. Außerdem befindet sich die Datei oft unverschlüsselt auf einem Datenträger – eine nicht verschlüsselte Festplatte oder ein USB-Stick ist schnell ausgelesen. Deshalb sollten Datenträger mit entsprechenden Systemen wie z. B. Bitlocker verschlüsselt werden. Des Weiteren ist darauf zu achten, dass speziell in Unternehmensnetzwerke die Vergabe von Berechtigungen auf einzelne Dateien oder Ordner nicht gerne gesehen wird, da einerseits der administrative Aufwand größer wird und andererseits sichergestellt sein muss, dass spezielle Dienstkonten (für Backup, Virenscanner u. ä.) trotzdem Zugriff auf die Datei haben.

Eine Lösung zum Setzen von Berechtigungen auf einzelne Dateien ist mit PowerShell zwar aufwendig, doch können Sie dies auch direkt in Ihre Skript integrieren und könnte wie folgt aussehen:

$credentialsACL = Get-Acl $credentialsFile            
             
$credentialsACL.SetAccessRuleProtection($true,$false)            
$aclPermissions = [System.Security.AccessControl.FileSystemRights]"FullControl"            
$aclInheritanceFlags = [System.Security.AccessControl.InheritanceFlags]::None            
$aclPropogationFlags = [System.Security.AccessControl.PropagationFlags]::None            
$aclType = [System.Security.AccessControl.AccessControlType]::Allow            
$aclUser = New-Object System.Security.Principal.NTAccount("Domain\User")            
             
$aclObject = New-Object System.Security.AccessControl.FileSystemAccessRule `
  ($aclUser, $aclPermissions, $aclInheritanceFlags, $aclPropogationFlags, $aclType)            
             
$credentialsACL.Access | Where-Object { $PSItem.IdentityReference -Like "*" } | ForEach-Object {            
  $credentialsACL.RemoveAccessRuleSpecific($PSItem)            
}            
             
$credentialsACL.AddAccessRule($aclObject)            
Set-Acl -Path $credentialsFile -AclObject $credentialsACL

Voraussetzung für das Setzen von Berechtigungen ist jedoch, dass Ihr Benutzerkonto dazu auch berechtigt ist. Dies ist speziell auf Datei-Server im Unternehmensnetzwerk nur selten der Fall.

Dateiverschlüsselung

Die Verschlüsselung einer Datei geht noch einen Schritt weiter als das Setzen von Berechtigungen auf das Dateisystem. Dabei wird eine gesamte Datei mit einem  Verschlüsselungsverfahren gesichert. Auch dafür stehen Ihnen wiederum mehrere Verfahren zur Verfügung:

  • Encrypted File System
  • Festplattenverschlüsselung (Bitlocker)
  • Verschlüsselungstools
  • Komprimierungstools mit Passwortschutz (wie z. B. WinZip oder 7Zip)
  • Verwendung eines eigenen Algorithmus zur Dateiverschlüsselung
Encrypted File System

Das NTFS-Dateisystem bietet Sicherheit auf Basis des Encrypted File System. Damit können einzelne Dateien oder komplette Verzeichnisse verschlüsselt werden. Ist der Benutzer angemeldet, der die Datei verschlüsselt hat, kann er diese verwenden, wie jede andere Datei auch. Es ist keine spezielle Anmeldung oder Passworteingabe erforderlich. Sie wird deshalb auch als transparente Dateiverschlüsselung bezeichnet. Das Anlegen einer verschlüsselten Datei oder eines Verzeichnisses kann entweder über die erweiterten Eigenschaften des Windows Explorers

image_thumb2

oder mit dem Windows Programm Cipher

oder PowerShell erfolgen:

$file = "C:\Temp\testfile.ps1"            
[IO.File]::Encrypt($file)            
[IO.File]::Decrypt($file)

Durch diesen Schutz wird die Datei so verschlüsselt, dass nur noch derjenige die Datei verwenden kann, der sie auch verschlüsselt hat. Möchten Sie eine solche Datei mit anderen teilen, müssen jedoch spezielle Einstellungen vorgenommen werden (siehe Sharing Encrypted Files).

Zum unerlaubten Zugriff auf mit EFS verschlüsselte Dateien gibt es mehrere Möglichkeiten (mehrfach im Internet dokumentiert z. B. unter http://en.wikipedia.org/wiki/Encrypting_File_System), jedoch ist der Aufwand dafür wesentlich höher.

Festplattenverschlüsselung (Bitlocker)

Bei Bitlocker handelt es sich um ein Verschlüsselungstool von Datenträgern. Damit kann sichergestellt werden, dass Datenträger, die Ihr Unternehmen verlassen, durch andere Personen nicht rekonstruiert werden können. Bei Bitlocker handelt es sich ebenfalls um eine transparente Dateiverschlüsselung, so dass Dateien, die auf einem mit Bitlocker gesicherten Laufwerk abgelegt wurden, nur durch entsprechend berechtigte Personen gelesen werden können.

Speichern sie beispielsweise alle Ihre Passwörter oder PowerShell-Skripte mit Passwörtern auf einem USB-Stick, so macht es Sinn, diesen mit Bitlocker vor unerlaubten Zugriff zu schützen. Jedoch sind Dateien nach dem Öffnen nicht mehr geschützt und können von jedem im Klartext gelesen werden. Auch das Kopieren solcher Dateien auf andere Datenträger, z. B. beim Austausch mit Kollegen, führt dazu, dass die Datei auf dem Zieldatenträger ungeschützt liegt. Somit stellt diese Lösung nur bedingt einen Schutz von Passwörtern dar.

Außer Bitlocker gibt es noch eine ganze Reihe weiterer Tools, mit denen Sie Festplatte oder andere Datenträger und einzelne Partitionen verschlüsseln können. Wikipedia stellt Ihnen hierzu eine ziemlich aktuell Liste zur Verfügung.

Verschlüsselungstools

Wenn man die Verschlüsselung von Windows nicht verwenden möchte, gibt es eine Reihe von freien und kostenpflichtigen Verschlüsselungstools im Internet. Die Sicherheit solcher Lösungen ist teilweise höher als die eingebaute Windows Sicherheit, jedoch nachdem diese nicht in Windows integriert ist, sind immer zusätzliche Maßnahmen (wie z. B. Passworteingabe) erforderlich, um die Datei verwenden zu können. Außerdem besitzen solche Tools oft keine Schnittstellen nach außen, so dass ein Zugriff per PowerShell nicht möglich ist (z. B. um eine Passwort-Datei) auszulesen.

Komprimierungstool

Des Weiteren gibt es einige Tools zur Komprimierung von Dateien, die das Verschlüsseln von Dateien mit Kennwort unterstützen. Doch ähnlich wie bei Verschlüsselungstools ist auch hier die Eingabe eines Kennworts erforderlich, bevor eine Datei aus einem Archiv verwendet werden kann. Der Vorteil dieser Lösung besteht darin, dass Sie einerseits ein Archiv mit allen schützenswerte PowerShell-Skripten anlegen und andererseits auch noch Plattenplatz sparen. Screenshot am Beispiel des Open Source Programms 7-Zip:

image

Eine andere Möglichkeit des Schutzes von Dokumenten bieten die Microsoft Office Produkte (z. B. Excel, Word)

image

image

Individuelle Dateiverschlüsselung

Neben der Verwendung von speziellen Tools kann man seine Dateien auch individuell verschlüsselt auf einem Datenträger speichern. Nachdem dieses Gebiet jedoch mehr in das vorhergehende Kapitel gehört, bin ich auch dort darauf eingehen.

Best Practices

  • Frage nur dann nach einem Passwort, wenn dies erforderlich ist. Ansonsten verwenden die integrierte Windows Authentifizierung.
  • Ist keine Windows Authentifizierung verfügbar bzw. müssen Sie mit alternativen Credentials (Benutzername und Passwort) arbeiten, verwenden Sie die PSCredentials-Klasse. Möchten Sie nur einen einzelne Zeichenfolge sichern, verwenden Sie die SecureString-Klasse.
  • Eine Konvertierung einer geschützten Zeichenkette in einen Klartext ist nur dann erforderlich, wenn das Zielsystem keine SecureStrings unterstützt. Verwenden Sie ansonsten immer SecureStrings. Ist eine Konvertierung in Klartext notwendig, kümmern Sie selbst um das Bereinigen z. B. durch Löschen der Variable
    Remove-Variable -Name $pwd -Force
  • Kennwörter sollten niemals im Klartext auf Festplatte, Registry oder anderen ungeschützten Speicher geschrieben werden. Verwenden Sie stattdessen immer einen SecureString.

Lösung

Nachdem ich mich in diesem Artikel vorab mit den theoretischen Grundlagen von Verschlüsselung beschäftigt und verschiedene praktische Umsetzungen aufgezeigt habe, möchte ich mich nachfolgend mit einer technischen Umsetzung beschäftigen, mit dem Sie in der Lage sind, Anmeldeinformationen oder andere schützenswerte Zeichenfolgen sicher mit PowerShell zu verwalten und von dort aus verwenden können.

Als Basis für diese Lösung habe ich mir zwei Funktionen zum Ver- und Entschlüsseln einer Zeichenkette definiert. Die verwendeten Algorithmen sind nur Beispiele und können von Ihnen gerne durch andere in diesem Artikel verwendete oder durch eigene Algorithmen ersetzt werden.

Die Funktionen zum Verwalten der Passwörter befindet sich in der Funktion Get-PwdStore. Um die “Klasse” einfach zu halten, habe ich nur zwei “Methoden” implementiert:

  • Credential: Speicherung bzw. Auslesen einer Benutzer/Schlüssel + Passwort-Kombination
  • Save: Speichern der Datei
function Encrypt {            
    param (            
        [String]$Text            
    )            
            
    return $(ConvertTo-SecureString -String $Text -AsPlainText -Force | ConvertFrom-SecureString -Key $Global:pwd)            
}            
            
function Decrypt {            
    param (            
        [String]$Text            
    )            
            
    $SecurePassword = $(ConvertTo-SecureString -String $Text -Key $Global:pwd)            
    $PasswordPointer = [Runtime.InteropServices.Marshal]::SecureStringToBSTR($SecurePassword)                
    $PlainTextPassword = [Runtime.InteropServices.Marshal]::PtrToStringAuto($PasswordPointer)            
    [Runtime.InteropServices.Marshal]::ZeroFreeBSTR($PasswordPointer)            
    return $PlainTextPassword            
}            
            
            
            
function Get-PwdStore {            
    [OutputType([PSObject])]              
    param (            
        [Parameter(Mandatory=$false)]            
        [String]$Library = $PSScriptRoot + "\PwdStore.xml"            
    )            
            
    $Script:_PwdStore = $null            
            
    if (Test-Path -Path $Library -PathType Leaf) {            
        $Script:_PwdStore = Import-Clixml -Path $Library            
            
        if ($Script:_PwdStore.Library -ne $Library) {            
            $Script:_PwdStore.Library = $Library            
        }            
    }            
            
    if ($Script:_PwdStore -eq $null) {            
        $Script:_PwdStore = new-object psobject -Property @{            
            Library = $Library            
            Credentials=@()            
        }            
    }            
            
    $Script:_PwdStore = $Script:_PwdStore | Add-Member -PassThru -MemberType ScriptMethod -Name Save -value {            
        [OutputType([Bool])]            
        param (            
            [Parameter(Mandatory=$false, Position=0)]            
            [String]$NewFilename = $null            
        )            
        if ($NewFilename.Length -gt 0) {            
            $This.Library = $NewFilename            
        }            
        $Script:_PwdStore | Export-Clixml -Path $This.Library            
        return $true            
    }            
                
    $Script:_PwdStore = $Script:_PwdStore | Add-Member -PassThru -MemberType ScriptMethod -Name Credential -Value {            
        param (            
            [Parameter(Mandatory=$true, Position=0)]            
            [String]$Username = $null,            
            [Parameter(Mandatory=$false, Position=1)]            
            [String]$Password = $null            
        )            
            
        foreach($c in $Script:_PwdStore.Credentials) {            
            if ($c.Owner -eq "$env:UserDomain\$env:USERNAME") {            
                if ($Username -eq $c.Username) {            
                    if ($Password.Length -eq 0) {            
                        return $(Decrypt -Text $c.Password)            
                    }            
                    elseif ($(Encrypt -Text $Password) -ne $c.Password) {            
                        $c.Password = Encrypt -Text $Password            
                        return            
                    }            
                    else {            
                        return            
                    }            
                }            
            }            
        }            
        $cObject = new-object psobject -Property @{            
            Owner = "$env:UserDomain\$env:USERNAME"            
            Username = $Username            
            Password = Encrypt -Text $Password            
        }                   
        $Script:_PwdStore.Credentials += $cObject            
    }            
                    
    return $Script:_PwdStore            
}

Die Verwendung dieser Funktionen ist sehr einfach und ich möchte Sie Ihnen am folgenden kleinen Beispiel erläutern:

$x = Get-PwdStore -Library "c:\temp\PwdStore.xml"            
            
$Global:pwd = [Text.Encoding]::UTF8.GetBytes([System.Security.Principal.WindowsIdentity]::GetCurrent().User.AccountDomainSid.Value.Substring(0,32))            
            
$x.Credential("Sylvio", "geheim")            
$x.Credential("Test"  , "pwd1")            
            
$x.Credential("Sylvio")            
            
$x.Save()
  1. Zeile: Anlegen bzw. Öffnen der Passwort-Datei
  2. Zeile: Für den von mir verwendeten Verschlüsselungsalgorithmus benötige ich einen MasterKey, den ich mir einfach aus der SID des aktuell  Benutzers angemeldeten Benutzers generiere. Der Vorteil dieser Methode ist, dass dadurch die Passwort-Datei von jedem beliebigen Computer aus geöffnet werden kann. Der größere Nachteil ist jedoch, dass die SID eines Benutzers kein Geheimnis ist und jeder, der den Verschlüsselungsalgorithmus kennt, kann somit auf die Passwörter zugreifen (siehe dazu Hinweisbox am Ende dieses Abschnitts.
  3. Zeile: Speichern von neuen Credentials bestehend aus einem Schlüsselwert (z. B. den Benutzernamen: hier Sylvio) und einer zu verschlüsselden Zeichenfolge (z. B. Passwort: hier geheim).
  4. Zeile: Speichern von neuen Credentials bestehend aus einem Schlüsselwert (z. B. den Benutzernamen: hier Test) und einer zu verschlüsselden Zeichenfolge (z. B. Passwort: hier pwd1).
  5. Zeile: Abfragen der Credentials auf Basis des Schlüsselwertes (in dem Beispiel “Sylvio”) –> zurückgeliefert wird das dazugehörende entschlüsselte Kennwort
  6. Speichern der Datei

Bei der Zählung der Zeilen haben ich Leerzeilen nicht beachtet.

Führt man das Script nun aus, erhält man folgendes Ergebnis:

image

In dem dargestellten Screenshot habe ich im Vergleich zum vorausgehenden Script noch zusätzliche Befehle am Ende eingebaut, um Ihnen zu demonstrieren, dass die gespeicherten Credentials auch abrufbar sind, wenn sie nicht erst vorher geschrieben worden sind und das ein Aufruf der Credentials-“Methode” ohne Parameter Ihnen den Usernamen/Key, den User, der die Daten gespeichert hat und das verschlüsselte Password zurückliefert. Für eine höhere Sicherheit besteht jedoch auch die Möglichkeit, zusätzlich noch Username und Owner ebenfalls zu verschlüsseln.

Hinweis: Die große Herausforderung bei der Verschlüsselung besteht darin, dass entweder ein solcher Algorithmus an ein definierte Kombination aus Windows User und Computer gebunden ist oder man ein Passwort zur Entschlüsselung verwenden muss. Leider bietet das Active Directory oder ein anderer zentraler standardmäßig verfügbarer Windows Dienst keine Möglichkeit zu einem eindeutigen Windows User ein eindeutigen geheimen Schlüssel zu liefern. Eine mögliche Alternativen für ein solches System ist, dass man sich einen eigenen Service programmiert, der auf einem bestimmten Computer läuft und die dortige lokale DPAPI verwendet.

Zusammenfassung

Das Thema ist spannend und sehr interessant – speziell unter den aktuell bekannt gewordenen Ausspähaffären des amerikanischen und britischen Geheimdienst (NSA und GCHQ) und deren Zusammenarbeit mit dem BND z. B. auch bei der Ausspähung deutscher Industriegeheimnisse. Doch auch Hacker weltweit sind sehr aktiv darin, sind Zugang zu vertraulichen Informationen zu beschaffen, um diese dann meistbietend zu verkaufen. Die größte Gefahr kommt jedoch oft aus dem eigenen Unternehmen und dem unsachgemäßen Umgang mit Informationen/Dokumenten. Deshalb sollte dieses Thema nicht auf die leichte Schulter genommen werden und jeder muss sich der Risiken bewusst sein, wenn er Passwörter ungeschützt abspeichert.

Dieser Artikel war weder dazu gedacht Ihnen einen vollständigen Überblick über das Thema “Verschlüsselung” zu geben noch Ihnen alle Möglichkeiten der PowerShell in Verbindung mit Passwörtern aufzuzeigen. Mit immer leistungsfähigen Computern wird es in Zukunft immer nur eine Frage der Zeit sein, bis ein Algorithmus geknackt ist (http://www.pcwelt.de/ratgeber/Verschluesselung_in_der_Zukunft_-_Was_ist_wirklich_sicher_-Risiken___Schwaechen-7789820.html). Dies soll Sie jedoch nicht davon abhalten, wenigstens ein Mindestmaß an Sicherheit in Ihre Programme zu integrieren und ich hoffe, dass ich Ihnen mit diesem Artikel die eine oder andere Anregung geliefert habe.

Die in diesem Blogartikel verwendeten Code-Schnipsel sind teilweise selbst erstellt, bestehender Code aus dem Internet wurde angepasst/weiterentwickelt oder direkt verwendet. Alle diese Code-Ausschnitte habe ich getestet und sie haben das von mir erwartete und hier dokumentierte Ergebnis geliefert. Inwieweit diese auch in Ihrer Umgebung funktionieren, kann ich jedoch nicht garantieren. Ich bin aber gerne bereit Sie bei der Analyse von Fehlern und/oder Beseitigung von Problemen zu unterstützen.

Sonstiges

Wie so oft bei der Recherche nach interessantem Material für diesen Beitrag bin ich auch wieder auf Lösungen oder Ideen gestoßen, die zwar direkt nichts mit dem Thema zu tun haben. Trotzdem möchte ich Euch dies nicht vorenthalten.

Passwortgenerator

Für einige Anwendungsfälle ist es erforderlich ein Passwort zu setzen, das nach außen nicht bekannt sein muss. Nun können Sie sich entweder aufwendig einen Passwortgenerator bauen oder Sie verwenden einfach eine bereits bestehende Klasse auf dem .NET Framework.

Im folgenden Beispiel wird ein Passwort mit 16 Zeichen (inkl. mindestens vier Sonderzeichen) generiert und an das SQL Server Setup übergeben.

Add-Type -AssemblyName "System.Web"            
$SAPassword = [System.Web.Security.Membership]::GeneratePassword(16,4)            
.\setup.exe /CONFIGURATIONFILE=$ConfigFile /SAPASSWORD=$SAPassword

Wer es etwas komplexer mag, kann auch folgendes Beispiel verwenden, das mehr Konfigurationsmöglichkeiten in der Auswahl der Zeichen zur Verfügung stellt:

Function New-Password {             
    [OutputType([String])]             
                
    Param(              
        [int]$length,             
        [alias("U")]             
        [Switch]$Uppercase,              
        [alias("L")]             
        [Switch]$LowerCase,              
        [alias("N")]             
        [Switch]$Numeric,              
        [alias("S")]             
        [Switch]$Symbolic              
    )             
                     
    If ($Uppercase) {$CharPool += ([char[]](65..90))}             
    If ($LowerCase) {$CharPool += ([char[]](97..122))}             
    If ($Numeric) {$CharPool += ([char[]](48..57))}             
    If ($Symbolic) {$CharPool += ([char[]](33..47))             
                    $CharPool += ([char[]](122..200))}             
                     
    If ($CharPool -eq $null) {             
        Throw 'You must select at least one of the parameters "Uppercase" "LowerCase" "Numeric" or "Symbolic"'             
    }             
             
    [String]$Password =  (Get-Random -InputObject $CharPool -Count $length) -join ''              
                     
    return $Password             
}
Base64

Das Thema gehört eigentlich nicht wirklich in diesen Abschnitt, da es sich bei Base64 um kein Verschlüsselungsverfahren handelt, sondern alle Zeichen einer Zeichenkette werden in ein lesbares, Codepage-unabhängiges ASCII-Format konvertiert. Dies sieht auf den ersten Blick unlesbar aus, doch da die Zeichenkette jederzeit reproduziert werden kann, ist sie zum Schutz eines PowerShell-Skript nicht geeignet (oder wenigsten nur vor solchen Leuten, die dieses Format nicht kennen bzw. diesen Artikel nicht gelesen haben).

Im folgenden Beispiel wird im ersten Schritt ein PowerShell-Skript Base64-codiert und in einem zweiten Schritt als PowerShell-Skript ausgeführt. Im letzten Schritt wird das codierte Script wieder im Klartext ausgegeben.

$command= [System.IO.File]::ReadAllText("C:\temp\Test.ps1")            
$bytes= [System.Text.Encoding]::Unicode.GetBytes($command)            
$encodedCommand = [System.Convert]::ToBase64String($bytes)            
$encodedCommand            
            
powershell.exe –EncodedCommand $encodedCommand            
            
powershell.exe -EncodedCommand VwByAGkAdABlAC0ASABvAHMAdAAgACIAQgBhAHMAZQA2ADQAIABaAGUAaQBjAGgAZQBuAGsAZQB0AHQAZQAiAA==            
            
[System.Text.Encoding]::Unicode.GetString(([System.Convert]::FromBase64String($encodedCommand )))

Verwenden Sie nun lediglich die Zeichenfolge mit dem Base64-codierten String, so ist das Skript nicht mehr so ohne weiteres lesbar.

Limitierung: $encodedCommand.Length < 8191 Zeichen aufgrund der Längenbegrenzung der Befehlszeile (ab Windows Vista)

Hinweis: Dieses Verfahren eignet sich jedoch hervorragend dafür, die ExecutionPolicy auf einem System zu umgehen (als eines von vielen Verfahren), da PowerShell encodedCommands automatisch als ausführbar ansieht (unabhängig der festgelegten ExecutionPolicy). Testen Sie dafür einfach mal folgendes:

powershell.exe -executionpolicy restricted 
    -file "C:\temp\Test.ps1"            
powershell.exe -executionpolicy restricted 
    -EncodedCommand VwByAGkAdABlAC0ASABvAHMAdAAgACIAQgBhAHMAZQA2ADQAIABaAGUAaQBjAGgAZQBuAGsAZQB0AHQAZQAiAA==

image

Advertisements
Über

Die IT-Welt wird immer komplexer und zwischen den einzelnen Komponenten gibt es immer mehr Abhängigkeiten. Nachdem ich durch meine tägliche Arbeit immer wieder vor der Herausforderung stehe, komplexe Probleme zu lösen, möchte ich diese Seite dafür verwenden, Euch den einen oder anderen Tipp zu geben, wenn Ihr vor ähnlichen Aufgabenstellungen steht.

Veröffentlicht in PowerShell, Sicherheit

Kommentar verfassen

Trage deine Daten unten ein oder klicke ein Icon um dich einzuloggen:

WordPress.com-Logo

Du kommentierst mit Deinem WordPress.com-Konto. Abmelden / Ändern )

Twitter-Bild

Du kommentierst mit Deinem Twitter-Konto. Abmelden / Ändern )

Facebook-Foto

Du kommentierst mit Deinem Facebook-Konto. Abmelden / Ändern )

Google+ Foto

Du kommentierst mit Deinem Google+-Konto. Abmelden / Ändern )

Verbinde mit %s

%d Bloggern gefällt das: