Wer sich per .Net dem Thema SecureString annähert wird sich irgendwann fragen: “Warum bekomm ich den hinterlegten String nicht einfach zurück?”. Tja die Sache liegt in der Natur des SecureString selber. Er soll sicher sein!
Fangen wir mal mit einer einfachen Geschichte an, man möchte ein Passwort sicher eingeben:
PS> $pw=Read-Host –AsSecureString
*****
PS>
Sieht schon mal gut aus. Meine Passworteingabe lautete Hallo und es wurden aber nur Sternchen angezeigt. Wer jetzt denkt, man bekommt bei Abfrage von $pw das Passwort der täuscht sich:
PS> $pw
System.Security.SecureString
OK, dann halt über ein Property?
PS> $pw | Get-Member
TypeName: System.Security.SecureString
Name MemberType Definition
—- ———- ———-
AppendChar Method void AppendChar(char c)
Clear Method void Clear()
Copy Method securestring Copy()
Dispose Method void Dispose(), void IDisposable.Dispose()
Equals Method bool Equals(System.Object obj)
GetHashCode Method int GetHashCode()
GetType Method type GetType()
InsertAt Method void InsertAt(int index, char c)
IsReadOnly Method bool IsReadOnly()
MakeReadOnly Method void MakeReadOnly()
RemoveAt Method void RemoveAt(int index)
SetAt Method void SetAt(int index, char c)
ToString Method string ToString()
Length Property int Length {get;}
Na supi! Nur Length ist als Property vorhanden. Na dann vielleicht ToString()?
PS> $pw.ToString()
System.Security.SecureString
Nene, das Ding soll ja sicher sein!
Übrigens was gibt Length zurück?
PS> $pw.Length
5
Das würde passen, “Hallo” hat genau fünf Buchstaben. Kurz gesagt, es gibt keine direkte Möglichkeit den String zurückzubekommen.
Wofür braucht man nun das Ding? Die aktuelle Beschreibung auf MSDN (V4.5), gibt keinen Tipp. http://msdn.microsoft.com/en-us/library/system.security.securestring.aspx
Also mal etwas recherchiert:
Sicher gibt es noch mehr Stellen, aber die kleine Auswahl sollte erst mal reichen.
OK, der Anwendungszweck gegeben, aber was kann ich damit anfangen? Vor allem im Zusammenhang mit Powershell? Ein erster Hinweis steht in der SecureString Doku auf MSDN:
Note that SecureString has no members that inspect, compare, or convert the value of a SecureString. The absence of such members helps protect the value of the instance from accidental or malicious exposure. Use appropriate members of the System.Runtime.InteropServices.Marshal class, such as the SecureStringToBSTR method, to manipulate the value of a SecureString object.
Aha, man muss also in die Niederungen der InteropServices absteigen. Dann legen wir gleich mal los:
PS> $BSTR=[System.Runtime.InteropServices.marshal]::SecureStringToBSTR($pw)
PS> $BSTR
39018744
PS> $pws = [System.Runtime.InteropServices.marshal]::PtrToStringAuto($BSTR)
PS> $pws
Hallo
PS> [System.Runtime.InteropServices.marshal]::ZeroFreeBSTR($BSTR)
Hier findet nun die ganze Show statt. Zuerst holt man sich mittels SecureStringToBSTR die ursprüngliche Eingabe und einen Pointer auf die Stelle, wo der String nun temporär abgelegt wird. Bei PtrToStringAuto wird der temporäre String ausgelesen, zurückgegeben und in $pws gespeichert. Mit ZeroFreeBSTR wird der temporäre String wieder freigegeben, bzw der temporäre Speicherplatz mit Nullen überschrieben (Secure!). Blöd nur, dass wir das Passwort nun in $pws haben, denn ab nun ist die Sache nicht mehr sicher!!
Hier noch der Link zu SecureStringToBSTR: http://msdn.microsoft.com/en-us/library/system.runtime.interopservices.marshal.securestringtobstr.aspx
Damit schließt sich der Kreis. Aber Moment, da geht noch mehr.
Passwörter in Dateien speichern
Es gibt nämlich die tollen Funktionen ConvertTo-SecureString und ConvertFrom-SecureString. Wer glaubt, damit ließe sich obige Prozedur abkürzen, der täuscht sich. Denn diese dienen nur dazu den SecureString zu serialisieren bzw. in einen Standard-String zu wandeln, um ihn z. B. in einer Datei speichern zu können.
Machen wir genau dies und speichern unseren String mal in einer Datei, dazu wandeln wir ihn zuerst, lassen ihn ausgeben und prüfen ob es ein String ist:
PS> $pwe = ConvertFrom-SecureString –SecureString $pw
PS> $pwe
01000000d08c9ddf0115d1118c7a00c04fc297eb01000000d13e0b3adb59f6469325c229ee5b0d520000000002000000000003660000c0000000100
00000d57efc5b5ccd1e52e71296ec9b91a3530000000004800000a0000000100000006b946cd8b1c877db8c8ae6f39f4932bd10000000858fb31747
0ef57f405d954a5857409a14000000e2154da0885013dc5dbf21097c61c1d429f1e8ce
PS> $pwe.GetType()
IsPublic IsSerial Name BaseType
——– ——– —- ——–
True True String System.Object
PS> Set-Content –Value $pwe –Path pwenc.txt
PS> type .\pwenc.txt
01000000d08c9ddf0115d1118c7a00c04fc297eb01000000d13e0b3adb59f6469325c229ee5b0d520000000002000000000003660000c0000000100
00000d57efc5b5ccd1e52e71296ec9b91a3530000000004800000a0000000100000006b946cd8b1c877db8c8ae6f39f4932bd10000000858fb31747
0ef57f405d954a5857409a14000000e2154da0885013dc5dbf21097c61c1d429f1e8ce
Perfekt. Damit hat man eine saubere Möglichkeit gefunden Passwörter sicher abzuspeichern. Um sicher zu gehen, dass dem so ist, muss man die Sache natürlich auch wieder rückwärts machen können.
SecureString-Passwörter aus Dateien auslesen
Die Geschichte ist einfach erzählt
PS> $pw=Get-Content pwenc.txt | ConvertTo-SecureString
Damit kann man $pw wieder bei SecureStringToBSTR, wie oben gezeigt anwenden und bekommt das gespeicherte Passwort im Klartext zurück.