Frage Warum wird char [] gegenüber String für Passwörter bevorzugt?


In Swing hat das Passwortfeld a getPassword() (kehrt zurück char[]) Methode statt der üblichen getText() (kehrt zurück String) Methode. Ebenso bin ich auf einen Vorschlag gestoßen, nicht zu verwenden String Passwörter handhaben.

Warum tut String eine Gefahr für die Sicherheit darstellen, wenn es um Passwörter geht? Es fühlt sich unbequem an zu verwenden char[].


2894
2018-01-16 14:20


Ursprung


Antworten:


Strings sind unveränderlich. Das bedeutet, sobald Sie das erstellt haben StringWenn ein anderer Prozess Speicher abladen kann, gibt es keinen Weg (abgesehen von Betrachtung) Sie können die Daten vorher loswerden Müllabfuhr tritt ein.

Mit einem Array können Sie die Daten explizit löschen, nachdem Sie damit fertig sind. Sie können das Array mit beliebigen Daten überschreiben, und das Kennwort ist auch vor der Garbage-Collection nirgendwo im System vorhanden.

Also ja, das ist ein Sicherheitsproblem - aber sogar Verwendung char[] reduziert nur das Zeitfenster für einen Angreifer und dies nur für diesen spezifischen Angriffstyp.

Wie in den Kommentaren angemerkt, ist es möglich, dass Arrays, die durch den Garbage Collector verschoben werden, Streukopien der Daten im Speicher hinterlassen. Ich glaube, das ist implementierungsspezifisch - der Garbage Collector kann räume alle Speicher ab, um so etwas zu vermeiden. Auch wenn es so ist, gibt es immer noch die Zeit, in der die char[] enthält die eigentlichen Zeichen als Angriffsfenster.


3714
2018-01-16 14:26



Während andere Vorschläge hier gelten, gibt es einen anderen guten Grund. Mit schlicht String Sie haben viel höhere Chancen versehentliches Drucken des Kennworts in Protokollen, Monitore oder ein anderer unsicherer Ort. char[] ist weniger anfällig.

Bedenken Sie:

public static void main(String[] args) {
    Object pw = "Password";
    System.out.println("String: " + pw);

    pw = "Password".toCharArray();
    System.out.println("Array: " + pw);
}

Drucke:

String: Password
Array: [C@5829428e

1078
2018-01-16 19:37



Um ein offizielles Dokument zu zitieren, das Anleitung zur Java-Kryptographie-Architektur sagt das über char[] gegen String Passwörter (über Passwort-basierte Verschlüsselung, aber das ist allgemein über Passwörter natürlich):

Es erscheint logisch, das Passwort in einem Objekt zu sammeln und zu speichern   vom Typ java.lang.String. Aber hier ist der Vorbehalt: Objects von   Art String sind unveränderlich, d. h. es sind keine Methoden definiert   erlaubt es Ihnen, den Inhalt von a zu ändern (überschreiben) oder auf Null zu setzen String   nach der Verwendung. Diese Funktion macht String Objekte ungeeignet für   Speichern von sicherheitsrelevanten Informationen wie Benutzerkennwörter. Sie   sollte immer sicherheitsrelevante Informationen in einem sammeln und speichern    char Array stattdessen.

Leitlinie 2-2 der Secure Coding Guidelines für die Programmiersprache Java, Version 4.0 sagt auch etwas ähnliches (obwohl es ursprünglich im Zusammenhang mit der Protokollierung steht):

Richtlinie 2-2: Protokollieren Sie keine hochsensiblen Informationen

Einige Informationen wie Sozialversicherungsnummern (SSN) und   Passwörter, ist sehr empfindlich. Diese Information sollte nicht aufbewahrt werden   für länger als notwendig, noch wo es gesehen werden kann, sogar durch   Administratoren. Zum Beispiel sollte es nicht an die Protokolldateien und gesendet werden   seine Präsenz sollte nicht durch Suchen erkennbar sein. Etwas vergänglich   Daten können in veränderlichen Datenstrukturen wie char-Arrays und   sofort nach Gebrauch gelöscht. Löschen von Datenstrukturen wurde reduziert   Effektivität auf typischen Java-Laufzeitsystemen beim Einziehen von Objekten   Speicher transparent für den Programmierer.

Diese Richtlinie hat auch Auswirkungen auf die Implementierung und Nutzung von   Bibliotheken auf niedrigerer Ebene, die kein semantisches Wissen über die Daten haben   sie haben es zu tun. Als Beispiel ein String-Parsing auf niedriger Ebene   Bibliothek kann den Text protokollieren, an dem sie arbeitet. Eine Anwendung kann einen SSN analysieren   mit der Bibliothek. Dies schafft eine Situation, in der die SSNs sind   verfügbar für Administratoren mit Zugriff auf die Protokolldateien.


610
2018-01-17 03:20



Zeichenfelder (char[]) kann nach der Verwendung gelöscht werden, indem jedes Zeichen auf Null und Strings nicht gesetzt wird. Wenn jemand das Speicherabbild irgendwie sehen kann, kann er ein Passwort im Klartext sehen, wenn Strings verwendet werden, aber wenn char[] wird verwendet, nachdem Daten mit 0 gelöscht wurden, ist das Passwort sicher.


305
2018-01-16 14:27



Einige Leute glauben, dass Sie den Speicher überschreiben müssen, der zum Speichern des Passworts verwendet wurde, sobald Sie es nicht mehr benötigen. Dies reduziert das Zeitfenster, in dem ein Angreifer das Passwort von Ihrem System lesen muss, und ignoriert vollständig, dass der Angreifer bereits genügend Zugriff darauf benötigt, um den JVM-Speicher zu entführen. Ein Angreifer mit so viel Zugriff kann Ihre Schlüsselereignisse abfangen, was dies völlig nutzlos macht (AFAIK, also korrigieren Sie mich bitte, wenn ich falsch liege).

Aktualisieren

Dank den Kommentaren muss ich meine Antwort aktualisieren. Offensichtlich gibt es zwei Fälle, in denen dies eine (sehr) geringfügige Sicherheitsverbesserung hinzufügen kann, da es die Zeit verringert, die ein Kennwort auf der Festplatte landen könnte. Trotzdem denke ich, dass es für die meisten Anwendungsfälle übertrieben ist.

  • Ihr Zielsystem ist möglicherweise schlecht konfiguriert oder Sie müssen davon ausgehen, dass dies der Fall ist, und Sie müssen bei Core-Dumps paranoid sein (dies kann gültig sein, wenn die Systeme nicht von einem Administrator verwaltet werden).
  • Ihre Software muss zu paranoid sein, um Datenlecks zu verhindern, wenn der Angreifer Zugriff auf die Hardware erhält TrueCrypt (abgesetzt), VeraCrypt, oder Chiffre.

Wenn möglich, würde die Deaktivierung von Core Dumps und der Auslagerungsdatei beide Probleme beheben. Sie benötigen jedoch Administratorrechte und können die Funktionalität reduzieren (weniger Arbeitsspeicher) und RAM aus einem laufenden System ziehen.


183
2018-01-16 14:32



  1. Strings sind unveränderlich Wenn Sie in Java das Passwort als einfachen Text speichern, wird es im Speicher verfügbar sein, bis der Garbage Collector es löscht und da Strings im String-Pool für die Wiederverwendbarkeit verwendet werden, besteht eine hohe Wahrscheinlichkeit, dass es für eine lange Zeit im Speicher bleibt stellt eine Sicherheitsbedrohung dar. Da jeder, der Zugriff auf Speicherabbild hat, kann das Kennwort im Klartext finden
  2. Java-Empfehlung verwenden getPassword() Methode von JPasswordField, die char [] und veraltet zurückgibt getText() Methode, die das Passwort im Klartext zurückgibt und den Sicherheitsgrund angibt.
  3. toString () Es besteht immer die Gefahr, Klartext in der Protokolldatei oder Konsole zu drucken, aber wenn Sie Array verwenden, drucken Sie nicht den Inhalt des Arrays, stattdessen wird der Speicherort gedruckt.

    String strPwd = "passwd";
    char[] charPwd = new char[]{'p','a','s','s','w','d'};
    System.out.println("String password: " + strPwd );
    System.out.println("Character password: " + charPwd );
    

    String-Passwort: passwd

    Zeichenpasswort: [C @ 110b2345

Abschließende Gedanken: Obwohl char [] nicht nur ausreicht, müssen Sie Inhalte löschen, um sicherer zu sein. Ich empfehle auch, mit Hash-Code oder verschlüsseltem Passwort anstelle von reinem Text zu arbeiten und es aus dem Speicher zu löschen, sobald die Authentifizierung abgeschlossen ist.


117
2017-12-27 20:22



Ich denke nicht, dass dies ein stichhaltiger Vorschlag ist, aber ich kann den Grund zumindest erraten.

Ich denke, die Motivation ist es, sicherzustellen, dass Sie alle Spuren des Passworts im Speicher sofort und mit Sicherheit löschen können, nachdem es verwendet wurde. Mit einem char[] Sie könnten jedes Element des Arrays mit einem Leerzeichen oder etwas sicher überschreiben. Sie können den internen Wert von a nicht bearbeiten String dieser Weg.

Aber das allein ist keine gute Antwort; warum nicht einfach einen Verweis auf die char[] oder String entkommt nicht? Dann gibt es kein Sicherheitsproblem. Aber die Sache ist das String Objekte können sein intern()in der Theorie und lebendig gehalten in dem konstanten Pool. Ich nehme an char[] verbietet diese Möglichkeit.


72
2018-01-16 14:27



Die Antwort wurde bereits gegeben, aber ich möchte ein Problem teilen, das ich kürzlich mit Java-Standardbibliotheken entdeckt habe. Während sie jetzt sorgfältig darauf achten, Passwort-Strings durch zu ersetzen char[] Überall (was natürlich eine gute Sache ist) scheinen andere sicherheitskritische Daten übersehen zu werden, wenn es darum geht, sie aus dem Gedächtnis zu löschen.

Ich denke an z.B. das Privat Schlüssel Klasse. Stellen Sie sich ein Szenario vor, in dem Sie einen privaten RSA-Schlüssel aus einer PKCS # 12-Datei laden und damit einen Vorgang ausführen möchten. In diesem Fall würde es Ihnen nicht viel helfen, das Passwort zu schnüffeln, solange der physische Zugriff auf die Schlüsseldatei ordnungsgemäß eingeschränkt ist. Als Angreifer wären Sie viel besser dran, wenn Sie den Schlüssel direkt anstelle des Passworts erhalten würden. Die gewünschten Informationen können vielfältig durchleuchtet werden, Core-Dumps, eine Debugger-Sitzung oder Auslagerungsdateien sind nur einige Beispiele.

Und wie sich herausstellt, gibt es nichts, was Sie die privaten Informationen von a PrivateKey aus dem Speicher, da es keine API gibt, mit der Sie die Bytes löschen können, die die entsprechenden Informationen bilden.

Das ist eine schlimme Situation Papier- beschreibt, wie dieser Umstand möglicherweise ausgenutzt werden könnte.

Die OpenSSL-Bibliothek überschreibt zum Beispiel kritische Speicherbereiche, bevor private Schlüssel freigegeben werden. Da Java eine Garbage-Collection ist, benötigen wir explizite Methoden, um private Informationen für Java-Schlüssel zu löschen und ungültig zu machen, die unmittelbar nach der Verwendung des Schlüssels angewendet werden.


58
2018-01-19 19:39



Wie Jon Skeet feststellt, gibt es keinen anderen Weg, als die Reflexion zu benutzen.

Wenn Reflektion jedoch eine Option für Sie ist, können Sie dies tun.

public static void main(String[] args) {
    System.out.println("please enter a password");
    // don't actually do this, this is an example only.
    Scanner in = new Scanner(System.in);
    String password = in.nextLine();
    usePassword(password);

    clearString(password);

    System.out.println("password: '" + password + "'");
}

private static void usePassword(String password) {

}

private static void clearString(String password) {
    try {
        Field value = String.class.getDeclaredField("value");
        value.setAccessible(true);
        char[] chars = (char[]) value.get(password);
        Arrays.fill(chars, '*');
    } catch (Exception e) {
        throw new AssertionError(e);
    }
}

wenn ausgeführt

please enter a password
hello world
password: '***********'

Hinweis: Wenn das Zeichen char [] der Zeichenkette als Teil eines GC-Zyklus kopiert wurde, besteht die Möglichkeit, dass sich die vorherige Kopie irgendwo im Speicher befindet.

Diese alte Kopie würde nicht in einem Heap-Dump erscheinen, aber wenn Sie direkten Zugriff auf den rohen Speicher des Prozesses haben, könnten Sie ihn sehen. Im Allgemeinen sollten Sie vermeiden, dass jemand einen solchen Zugang hat.


41
2017-07-08 09:26