it-swarm.com.de

Ist SecureString jemals in einer C # -Anwendung praktisch?

Sie können mich gerne korrigieren, wenn meine Annahmen hier falsch sind, aber lassen Sie mich erklären, warum ich frage.

Aus MSDN entnommen, ein SecureStringname__:

Stellt Text dar, der vertraulich behandelt werden sollte. Der Text wird aus Datenschutzgründen verschlüsselt, wenn er verwendet wird, und aus dem Computerspeicher gelöscht, wenn er nicht mehr benötigt wird.

Ich verstehe, dass es absolut sinnvoll ist, ein Passwort oder andere private Informationen in einem SecureStringüber einem System.String zu speichern, da Sie steuern können, wie und wann es tatsächlich im Speicher gespeichert wird, weil ein System.String:

ist unveränderlich und kann, wenn es nicht mehr benötigt wird, nicht programmgesteuert für die Speicherbereinigung eingeplant werden. Das heißt, die Instanz ist nach ihrer Erstellung schreibgeschützt und es ist nicht möglich, vorherzusagen, wann die Instanz aus dem Computerspeicher gelöscht wird. Wenn ein String-Objekt vertrauliche Informationen wie ein Passwort, eine Kreditkartennummer oder persönliche Daten enthält, besteht folglich die Gefahr, dass die Informationen nach ihrer Verwendung angezeigt werden, da Ihre Anwendung die Daten nicht aus dem Computerspeicher löschen kann.

Bei einer GUI-Anwendung (z. B. einem ssh-Client) muss jedoch der Name der SecureStringaus einemSystem.String erstellt werden. Alle Textsteuerelementeverwenden eine Zeichenfolge als zugrunde liegenden Datentyp.

Dies bedeutet, dass jedes Mal, wenn der Benutzer eine Taste drückt, die alte Zeichenfolge verworfen wird und eine neue Zeichenfolge erstellt wird, um den Wert im Textfeld darzustellen, auch wenn eine Kennwortmaske verwendet wird.Und wir können nicht kontrollieren, wann oder ob einer dieser Werte aus dem Speicher gelöscht wird.

Jetzt ist es Zeit, sich beim Server anzumelden. Erraten Sie, was?Sie müssen einen String über die Verbindung übergeben, um sich zu authentifizieren. Konvertieren wir also unseren SecureStringin einen System.String.... und jetzt haben wir eine Zeichenfolge auf dem Heap, die nicht zwingen kann, die Garbage Collection zu durchlaufen (oder 0en in den Puffer zu schreiben).

Mein Punkt ist : egal was Sie irgendwo auf der Linie tun, ist, dass SecureStringname__goingin einen System.String umgewandelt werden soll, was bedeutet, dass er zumindest auf dem Haufen bei einigen vorhanden sein wird Punkt (ohne Garantie der Müllabfuhr).

Mein Punkt ist nicht : ob es Möglichkeiten gibt, das Senden einer Zeichenfolge an eine SSH-Verbindung zu umgehen, oder zu umgehen, dass ein Steuerelement eine Zeichenfolge speichert (ein benutzerdefiniertes Steuerelement erstellen). Bei dieser Frage können Sie "ssh connection" durch "login form", "registration form", "payment form", "food-you-would-feed-your-puppy-but-not-your-children form" ersetzen. usw.

  • Ab wann ist die Verwendung eines SecureStringpraktisch?
  • Lohnt sich jemals die zusätzliche Entwicklungszeit, um die Verwendung eines System.String-Objekts vollständig zu beseitigen?
  • Ist es der Sinn von SecureStringname__, einfach die Zeit zu reduzieren, die sich ein System.String auf dem Heap befindet (wodurch das Risiko verringert wird, in eine physische Auslagerungsdatei zu wechseln)?
  • Wenn ein Angreifer bereits über die Mittel für eine Heap-Inspektion verfügt, verfügt er höchstwahrscheinlich entweder bereits über die Mittel zum Lesen von Tastenanschlägen, oder (B) verfügt bereits überphysisch über die Maschine... Also Würde die Verwendung eines SecureStringnamens__ ihn davon abhalten, auf die Daten zuzugreifen?
  • Ist das nur "Sicherheit durch Dunkelheit"?

Tut mir leid, wenn ich die Fragen zu dick auflege, die Neugier hat mich gerade überwunden. Fühlen Sie sich frei, einige oder alle meine Fragen zu beantworten (oder mir zu sagen, dass meine Annahmen völlig falsch sind). :)

210
Steven Jeffries

Es gibt tatsächlich sehr praktische Anwendungen von SecureString

Wissen Sie, wie oft ich solche Szenarien gesehen habe? (die Antwort ist: viele!):

  • Ein Kennwort wird versehentlich in einer Protokolldatei angezeigt.
  • Ein Kennwort wird irgendwo angezeigt - Sobald eine GUI eine Befehlszeile der ausgeführten Anwendung zeigte und die Befehlszeile aus einem Kennwort bestand. Oops.
  • Verwenden Sie den Memory Profiler, um die Software mit Ihrem Kollegen zu profilieren. Kollege sieht Ihr Passwort im Speicher. Klingt unwirklich? Überhaupt nicht.
  • Ich habe einmal RedGate-Software verwendet, die im Falle von Ausnahmen den "Wert" lokaler Variablen erfassen kann, erstaunlich nützlich. Ich kann mir jedoch vorstellen, dass es versehentlich "String-Passwörter" protokolliert.
  • Ein Absturzabbild, das ein Zeichenfolge-Kennwort enthält.

Wissen Sie, wie Sie all diese Probleme vermeiden können? SecureString. Es stellt im Allgemeinen sicher, dass Sie keine dummen Fehler als solche machen. Wie vermeidet es das? Stellen Sie sicher, dass das Kennwort im nicht verwalteten Speicher verschlüsselt ist und der tatsächliche Wert nur dann verfügbar ist, wenn Sie sich zu 90% sicher sind, was Sie tun.

In dem Sinn funktioniert SecureString ziemlich einfach:

1) Alles ist verschlüsselt

2) Benutzer ruft AppendChar auf

3) Entschlüssle alles in UNMANAGED MEMORY und füge den Charakter hinzu

4) Verschlüsseln Sie alles erneut in UNMANAGED MEMORY.

Was ist, wenn der Benutzer Zugriff auf Ihren Computer hat? Würde ein Virus Zugriff auf alle SecureStrings erhalten? Ja. Alles, was Sie tun müssen, ist, sich in RtlEncryptMemory einzuhaken, wenn der Speicher entschlüsselt wird. Sie erhalten den Speicherort der unverschlüsselten Speicheradresse und lesen diese aus. Voila! In der Tat könnten Sie einen Virus erstellen, der ständig nach SecureString sucht und alle Aktivitäten damit protokolliert. Ich sage nicht, dass dies eine leichte Aufgabe sein wird, aber es kann getan werden. Wie Sie sehen, ist die "Mächtigkeit" von SecureString vollständig verschwunden, sobald sich ein Benutzer/Virus in Ihrem System befindet.

Sie haben ein paar Punkte in Ihrem Beitrag. Wenn Sie einige der Benutzeroberflächen-Steuerelemente verwenden, die intern ein "Zeichenfolge-Kennwort" enthalten, ist die Verwendung von SecureString nicht sehr nützlich. Trotzdem kann es vor einigen Dummheiten schützen, die ich oben aufgeführt habe. 

Wie andere bereits erwähnt haben, unterstützt WPF PasswordBox, das SecureString intern über seine SecurePassword - Eigenschaft verwendet.

Das Endergebnis ist; Wenn Sie vertrauliche Daten (Kennwörter, Kreditkarten usw.) haben, verwenden Sie SecureString. Dies ist, was C # Framework folgt. Beispielsweise speichert die Klasse NetworkCredential das Kennwort als SecureString. Wenn Sie this betrachten, können Sie im .NET-Framework von SecureString über ~ 80 verschiedene Verwendungen sehen.

In vielen Fällen müssen Sie SecureString in string konvertieren, da einige API dies erwarten. 

Das übliche Problem ist entweder: 

  1. Die API ist GENERIC. Es weiß nicht, dass es sensible Daten gibt. 
  2. Die API weiß, dass es sich um sensible Daten handelt, und verwendet "string" - das ist nur ein schlechtes Design.

Sie haben einen guten Punkt angesprochen: Was passiert, wenn SecureString in string konvertiert wird? Dies kann nur aufgrund des ersten Punktes geschehen. Z.B. Die API weiß nicht, dass es sich um sensible Daten handelt. Ich habe das nicht persönlich gesehen. String aus SecureString herauszuholen ist nicht so einfach. 

Es ist aus einem einfachen Grund nicht einfach; Es war nie beabsichtigt, den Benutzer SecureString in String konvertieren zu lassen, wie Sie sagten: GC tritt ein. Wenn Sie sich dabei sehen, müssen Sie einen Schritt zurücktreten und sich fragen: Warum mache ich das überhaupt oder brauche ich wirklich deswegen?

Es gibt einen interessanten Fall, den ich gesehen habe. Die WinApi-Funktion LogonUser verwendet nämlich LPTSTR als Kennwort. Dies bedeutet, dass Sie SecureStringToGlobalAllocUnicode aufrufen müssen. Im Grunde erhalten Sie ein unverschlüsseltes Kennwort, das sich in einem nicht verwalteten Speicher befindet. Sie müssen das loswerden, sobald Sie fertig sind:

// Marshal the SecureString to unmanaged memory.
IntPtr rawPassword = Marshal.SecureStringToGlobalAllocUnicode(password);
try
{
   //...snip...
}
finally 
{
   // Zero-out and free the unmanaged string reference.
   Marshal.ZeroFreeGlobalAllocUnicode(rawPassword);
}

Sie können die SecureString-Klasse jederzeit um eine Erweiterungsmethode wie ToEncryptedString(__SERVER__PUBLIC_KEY) erweitern, die Ihnen eine string-Instanz von SecureString gibt, die mit dem öffentlichen Schlüssel des Servers verschlüsselt wird. Nur der Server kann es dann entschlüsseln. Problem gelöst: In der Garbage Collection wird nie die "ursprüngliche" Zeichenfolge angezeigt, da Sie sie niemals im verwalteten Speicher verfügbar machen. Genau das wird in PSRemotingCryptoHelper (EncryptSecureStringCore(SecureString secureString)) gemacht.

Und als etwas sehr Fast-bezogenes: Mono SecureString verschlüsselt überhaupt nicht. Die Implementierung wurde auskommentiert, weil ..warten Sie darauf .. "Es verursacht irgendwie Testbrüche von Nunits" , was zu meinem letzten Punkt führt:

SecureString wird nicht überall unterstützt. Wenn die Plattform/Architektur SecureString nicht unterstützt, wird eine Ausnahme angezeigt. Es gibt eine Liste von Plattformen, die in der Dokumentation unterstützt werden. 

221

Das sind wenige Punkte in Ihren Annahmen. 

Die SecureString-Klasse hat zunächst keinen String-Konstruktor. Um ein Objekt anzulegen, ordnen Sie ein Objekt zu und hängen dann die Zeichen an. 

Im Falle einer GUI oder einer Konsole können Sie jede gedrückte Taste sehr einfach an eine sichere Zeichenfolge übergeben. 

Die Klasse ist so konzipiert, dass Sie versehentlich nicht auf den gespeicherten Wert zugreifen können. Das bedeutet, dass Sie die string nicht direkt als Passwort erhalten können. 

Wenn Sie es beispielsweise zur Authentifizierung über das Web verwenden möchten, müssen Sie geeignete Klassen verwenden, die auch sicher sind. 

Im .NET-Framework gibt es einige Klassen, die SecureString verwenden können 

  • Das PasswordBox-Steuerelement von WPF speichert das Kennwort intern als SecureString.
  • Die Eigenschaft Password von System.Diagnostics.ProcessInfo ist ein SecureString.
  • Der Konstruktor für X509Certificate2 akzeptiert einen SecureString für das Kennwort. 

(Mehr)

Zusammenfassend kann die SecureString-Klasse nützlich sein, erfordert jedoch mehr Aufmerksamkeit vom Entwickler. 

All dies, mit Beispielen, ist in der MSDN-Dokumentation von SecureString beschrieben. 

Ein SecureString ist nützlich, wenn:

  • Sie erstellen sie zeichenweise (z. B. aus Konsoleneingaben) oder beziehen sie von einer nicht verwalteten API

  • Sie verwenden es, indem Sie es an eine nicht verwaltete API (SecureStringToBSTR) übergeben.

Wenn Sie es jemals in eine verwaltete Zeichenfolge konvertieren, haben Sie seinen Zweck besiegt.

UPDATE als Antwort auf Kommentar

... oder BSTR wie du es erwähnt hast, was nicht sicherer erscheint

Nach der Umwandlung in ein BSTR kann die nicht verwaltete Komponente, die das BSTR verwendet, den Speicher auf Null setzen. Nicht verwalteter Speicher ist in dem Sinne sicherer, dass er auf diese Weise zurückgesetzt werden kann.

Es gibt jedoch nur sehr wenige APIs in .NET Framework, die SecureString unterstützen. Sie können also zu Recht sagen, dass es heute von sehr begrenztem Wert ist.

Der Hauptanwendungsfall, den ich sehen würde, liegt in einer Client-Anwendung, bei der der Benutzer einen hochsensiblen Code oder ein Kennwort eingeben muss. Die Benutzereingabe könnte zeichenweise verwendet werden, um einen SecureString zu erstellen, und dann an eine nicht verwaltete API übergeben, die das BSTR, das es nach der Verwendung erhält, auf Null setzt. Ein nachfolgender Speicherabzug enthält keine vertrauliche Zeichenfolge.

In einer Serveranwendung ist es schwer zu erkennen, wo dies sinnvoll ist.

UPDATE 2

Ein Beispiel für eine .NET-API, die einen SecureString akzeptiert, ist dieser Konstruktor für die X509Certificate-Klasse . Wenn Sie mit ILSpy oder ähnlichem spelunken, werden Sie feststellen, dass der SecureString intern in einen nicht verwalteten Puffer (Marshal.SecureStringToGlobalAllocUnicode) konvertiert wird, der dann mit (Marshal.ZeroFreeGlobalAllocUnicode) auf Null gesetzt wird. 

10
Joe

Wie Sie richtig erkannt haben, bietet SecureString einen spezifischen Vorteil gegenüber string: deterministisches Löschen. Es gibt zwei Probleme mit dieser Tatsache: 

  1. Wie andere bereits erwähnt haben und wie Sie selbst bemerkt haben, reicht dies nicht aus. Sie müssen sicherstellen, dass jeder Schritt des Prozesses (einschließlich Abrufen der Eingabe, Erstellen der Zeichenfolge, Verwendung, Löschen, Transport usw.) ohne den Zweck der Verwendung von SecureString erfolgt. Dies bedeutet, dass Sie darauf achten müssen, niemals eine von GC verwaltete unveränderliche string oder einen anderen Puffer zu erstellen, in dem die vertraulichen Informationen gespeichert werden (oder Sie müssen auch that im Auge behalten). In der Praxis ist dies nicht immer leicht zu erreichen, da viele APIs nur eine Möglichkeit bieten, mit string zu arbeiten, nicht mit SecureString. Und auch wenn Sie alles richtig machen ... 
  2. SecureString schützt vor sehr spezifischen Angriffen (und für manche ist es nicht einmal so zuverlässig). Mit SecureString können Sie beispielsweise das Zeitfenster verkleinern, in dem ein Angreifer den Speicher Ihres Prozesses sichern und die vertraulichen Informationen erfolgreich extrahieren kann (wiederum, wie Sie es richtig angegeben haben), in der Hoffnung, dass das Fenster für den Angreifer zu klein ist Eine Momentaufnahme Ihres Gedächtnisses zu machen, wird überhaupt nicht als Sicherheit betrachtet.

Wann sollten Sie es verwenden? Nur wenn Sie mit etwas arbeiten, das es Ihnen ermöglicht, mit SecureString für all Ihre Bedürfnisse zu arbeiten, sollten Sie trotzdem bedenken, dass dies nur unter bestimmten Umständen sicher ist.

Ich möchte diesen Punkt ansprechen:

Wenn ein Angreifer bereits über die Mittel für eine Haufeninspektion verfügt, hat er höchstwahrscheinlich entweder (A) bereits die Möglichkeit, Tastenanschläge zu lesen, oder (B) bereits physisch über die Maschine verfügenSecureString verhindern, dass sie trotzdem zu den Daten gelangen?

Ein Angreifer hat möglicherweise keinen vollständigen Zugriff auf den Computer und die Anwendung, kann jedoch auf einige Teile des Arbeitsspeichers des Prozesses zugreifen. Dies wird in der Regel durch Fehler wie Pufferüberläufe verursacht, wenn eine speziell konstruierte Eingabe dazu führen kann, dass die Anwendung einen Teil des Speichers freigibt oder überschreibt.

HeartBleed undichtes Gedächtnis

Nehmen Sie zum Beispiel Heartbleed. Speziell konstruierte Anforderungen können dazu führen, dass der Code dem Angreifer zufällige Teile des Arbeitsspeichers des Prozesses verfügbar macht. Ein Angreifer kann SSL-Zertifikate aus dem Speicher extrahieren. Das einzige, was er jedoch benötigt, ist die Verwendung einer fehlerhaften Anfrage.

In der Welt des verwalteten Codes werden Pufferüberläufe viel seltener zu einem Problem. Bei WinForms sind die Daten bereits unsicher gespeichert, und Sie können nichts dagegen tun. Dies macht den Schutz mit SecureString ziemlich unbrauchbar.

Die GUI can kann jedoch so programmiert werden, dass SecureString verwendet wird. In diesem Fall kann es sich lohnen, das Fenster für die Verfügbarkeit des Kennworts im Speicher zu reduzieren. Beispielsweise ist PasswordBox.SecurePassword from WPF vom Typ SecureString.

3
Athari

Der folgende Text wird aus dem statischen HP Fortify-Code-Analysator kopiert.

Abstract: Die Methode PassString () in PassGenerator.cs speichert sensible Daten auf unsichere Weise (d. H. In einer Zeichenfolge), wodurch das Extrahieren der Daten über die Heapprüfung ermöglicht wird.

Erläuterung: Im Speicher gespeicherte vertrauliche Daten (wie Kennwörter, Sozialversicherungsnummern, Kreditkartennummern usw.) können verloren gehen, wenn sie in einem verwalteten String-Objekt Gespeichert sind. String-Objekte werden nicht angeheftet, sodass der Garbage Collector diese Objekte nach Belieben verschieben kann und Mehrere Kopien im Speicher belässt. Diese Objekte sind standardmäßig nicht verschlüsselt, sodass jeder, der den Speicher des Prozesses lesen kann, den Inhalt sehen kann. Wenn der Arbeitsspeicher des Prozesses auf die Festplatte ausgelagert wird, wird der unverschlüsselte Inhalt der Zeichenfolge Außerdem in eine Auslagerungsdatei geschrieben. Da String-Objekte nicht unveränderlich sind, kann das Entfernen des Werts eines Strings aus dem Speicher nur durch den CLR-Garbage-Collector erfolgen. Es ist nicht erforderlich, dass der Garbage Collector ausgeführt wird, es sei denn, der CLR-Speicher ist knapp. Daher gibt es keine Garantie dafür, wann die Garbage Collection stattfinden wird. Im Falle eines Anwendungsabsturzes kann ein Speicherabbild der Anwendung Sensible Daten enthalten.

Empfehlungen: Anstatt sensible Daten in Objekten wie Strings zu speichern, speichern Sie sie in einem SecureString-Objekt. Jedes Objekt speichert seinen Inhalt jederzeit in einem verschlüsselten .__-Format im Speicher.

2
vikrantx

Vor einiger Zeit musste ich eine C # -Schnittstelle für ein Java-Kreditkartenzahlungsgateway erstellen und eine kompatible Verschlüsselung mit sicheren Kommunikationsschlüsseln. Da die Java-Implementierung ziemlich spezifisch war, musste ich auf eine bestimmte Weise mit den geschützten Daten arbeiten. 

Ich fand dieses Design sehr einfach zu bedienen und einfacher als mit SecureString zu arbeiten… für diejenigen, die gerne verwenden… fühlen Sie sich frei, keine rechtlichen Einschränkungen :-). Beachten Sie, dass diese Klassen intern sind. Möglicherweise müssen Sie sie öffentlich machen.

namespace Cardinity.Infrastructure
{
    using System.Security.Cryptography;
    using System;
    enum EncryptionMethods
    {
        None=0,
        HMACSHA1,
        HMACSHA256,
        HMACSHA384,
        HMACSHA512,
        HMACMD5
    }


internal class Protected
{
    private  Byte[] salt = Guid.NewGuid().ToByteArray();

    protected byte[] Protect(byte[] data)
    {
        try
        {
            return ProtectedData.Protect(data, salt, DataProtectionScope.CurrentUser);
        }
        catch (CryptographicException)//no reason for hackers to know it failed
        {
#if DEBUG
            throw;
#else
            return null;
#endif
        }
    }

    protected byte[] Unprotect(byte[] data)
    {
        try
        {
            return ProtectedData.Unprotect(data, salt, DataProtectionScope.CurrentUser);
        }
        catch (CryptographicException)//no reason for hackers to know it failed
        {
#if DEBUG
            throw;
#else
            return null;
#endif
        }
    }
}


    internal class SecretKeySpec:Protected,IDisposable
    {
        readonly EncryptionMethods _method;

        private byte[] _secretKey;
        public SecretKeySpec(byte[] secretKey, EncryptionMethods encryptionMethod)
        {
            _secretKey = Protect(secretKey);
            _method = encryptionMethod;
        }

        public EncryptionMethods Method => _method;
        public byte[] SecretKey => Unprotect( _secretKey);

        public void Dispose()
        {
            if (_secretKey == null)
                return;
            //overwrite array memory
            for (int i = 0; i < _secretKey.Length; i++)
            {
                _secretKey[i] = 0;
            }

            //set-null
            _secretKey = null;
        }
        ~SecretKeySpec()
        {
            Dispose();
        }
    }

    internal class Mac : Protected,IDisposable
    {
        byte[] rawHmac;
        HMAC mac;
        public Mac(SecretKeySpec key, string data)
        {

            switch (key.Method)
            {
                case EncryptionMethods.HMACMD5:
                    mac = new HMACMD5(key.SecretKey);
                    break;
                case EncryptionMethods.HMACSHA512:
                    mac = new HMACSHA512(key.SecretKey);
                    break;
                case EncryptionMethods.HMACSHA384:
                    mac = new HMACSHA384(key.SecretKey);
                    break;
                case EncryptionMethods.HMACSHA256:
                    mac = new HMACSHA256(key.SecretKey);

                break;
                case EncryptionMethods.HMACSHA1:
                    mac = new HMACSHA1(key.SecretKey);
                    break;

                default:                    
                    throw new NotSupportedException("not supported HMAC");
            }
            rawHmac = Protect( mac.ComputeHash(Cardinity.ENCODING.GetBytes(data)));            

        }

        public string AsBase64()
        {
            return System.Convert.ToBase64String(Unprotect(rawHmac));
        }

        public void Dispose()
        {
            if (rawHmac != null)
            {
                //overwrite memory address
                for (int i = 0; i < rawHmac.Length; i++)
                {
                    rawHmac[i] = 0;
                }

                //release memory now
                rawHmac = null;

            }
            mac?.Dispose();
            mac = null;

        }
        ~Mac()
        {
            Dispose();
        }
    }
}

Microsoft empfiehlt nicht, SecureString für neuere Codes zu verwenden.

Aus der Dokumentation von SecureString-Klasse :

Wichtig

Es wird nicht empfohlen, die Klasse SecureString für die Neuentwicklung zu verwenden. Weitere Informationen finden Sie unter SecureString sollte nicht verwendet werden

Welches empfiehlt:

Verwenden Sie SecureString nicht für neuen Code. Berücksichtigen Sie beim Portieren von Code nach .NET Core, dass der Inhalt des Arrays nicht im Speicher verschlüsselt ist.

Der allgemeine Ansatz beim Umgang mit Anmeldeinformationen besteht darin, diese zu vermeiden und stattdessen andere Authentifizierungsmethoden wie Zertifikate oder Windows-Authentifizierung zu verwenden. auf GitHub.

0
SᴇM