it-swarm.com.de

Warum wird char [] String für Kennwörter vorgezogen?

In Swing hat das Kennwortfeld eine getPassword() (Returns char[]) -Methode anstelle der üblichen getText() (String) -Methode. Ebenso bin ich auf einen Vorschlag gestoßen, String nicht für die Handhabung von Passwörtern zu verwenden.

Warum stellt String eine Sicherheitsbedrohung für Kennwörter dar? Die Verwendung von char[] ist unpraktisch.

3086
Ahamed

Strings sind unveränderlich. Das heißt, wenn Sie einmal die Variable String erstellt haben und ein anderer Prozess Speicher ausgeben kann, gibt es keine Möglichkeit (abgesehen von reflect ), dass Sie die Daten vor garbage collection abfangen können.

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

Also ja, dieses ist ein Sicherheitsbedenken - aber selbst die Verwendung von char[] reduziert nur die Möglichkeit eines Angreifers, und dies gilt nur für diese spezielle Art von Angriff.

Wie in den Kommentaren erwähnt, ist es möglich, dass Arrays, die vom Garbage Collector verschoben werden, verstreute Kopien der Daten im Speicher belassen. Ich glaube, das ist implementierungsspezifisch - der Garbage Collector may löscht den gesamten Speicher, um diese Art von Dingen zu vermeiden. Selbst wenn es der Fall ist, gibt es immer noch die Zeit, in der der char[] die tatsächlichen Zeichen als Angriffsfenster enthält.

3941
Jon Skeet

Während andere Vorschläge hier gültig erscheinen, gibt es noch einen weiteren guten Grund. Mit plain String haben Sie wesentlich höhere Chancen, dass versehentlich das Kennwort in Protokolle gedruckt wird, Monitore oder andere unsichere Stellen. 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: [[email protected]
1136
Konrad Garus

Um ein offizielles Dokument zu zitieren, sagt der Java Cryptography Architecture-Leitfaden über char[] vs. String-Kennwörter (über kennwortbasierte Verschlüsselung, hier geht es jedoch im Allgemeinen um Kennwörter):

Es erscheint logisch, das Passwort in einem Objekt zu sammeln und zu speichern vom Typ Java.lang.String. Hier ist jedoch die Einschränkung: Objects von Typ String sind unveränderlich, d. h. es sind keine Methoden definiert, die Sie können den Inhalt einer String.__ ändern (überschreiben) oder auf Null setzen. nach Gebrauch. Diese Funktion macht String-Objekte für .__ ungeeignet. Speichern sicherheitsrelevanter Informationen wie z. B. Benutzerkennwörter. Sie sollte immer sicherheitsrelevante Informationen in einem .__ sammeln und speichern. stattdessen char Array.

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

Richtlinie 2-2: Keine hochsensiblen Informationen protokollieren

Einige Informationen, wie Sozialversicherungsnummern (SSNs) und Passwörter, ist sehr empfindlich. Diese Informationen sollten nicht aufbewahrt werden länger als nötig noch dort, wo es gesehen werden kann, auch durch Administratoren. Es sollte beispielsweise nicht an die Protokolldateien und .__ gesendet werden. Ihre Anwesenheit sollte nicht durch Suchvorgänge erkennbar sein. Einige vorübergehende Daten können in veränderlichen Datenstrukturen wie Char-Arrays und .__ gespeichert werden. sofort nach Gebrauch gelöscht. Das Löschen von Datenstrukturen hat .__ reduziert. Wirksamkeit auf typischen Java-Laufzeitsystemen, wenn Objekte in .__ verschoben werden. Speicher transparent für den Programmierer.

Diese Richtlinie hat auch Auswirkungen auf die Implementierung und Verwendung von untergeordnete Bibliotheken, die keine semantischen Kenntnisse der Daten haben Sie haben es zu tun. Beispiel: Eine Low-Level-Zeichenfolge Die Bibliothek protokolliert möglicherweise den Text, an dem sie arbeitet. Eine Anwendung kann eine SSN analysieren mit der Bibliothek. Dadurch entsteht eine Situation, in der die SSNs .__ sind. Administratoren mit Zugriff auf die Protokolldateien verfügbar.

639
Bruno

Zeichenarrays (char[]) können nach der Verwendung gelöscht werden, indem jedes Zeichen auf Null und Strings nicht gesetzt wird. Wenn jemand das Speicherabbild irgendwie sehen kann, wird bei Verwendung von Strings ein Kennwort im Klartext angezeigt. Wenn jedoch char[] verwendet wird, ist das Kennwort nach dem Löschen von Daten mit Nullen sicher.

326
alephx

Einige Leute glauben, dass Sie den Speicher für das Speichern des Passworts überschreiben müssen, wenn Sie es nicht mehr benötigen. Dies reduziert das Zeitfenster, in dem ein Angreifer das Kennwort von Ihrem System lesen muss, und ignoriert vollständig die Tatsache, dass der Angreifer bereits genügend Zugriff auf den Hijack-Speicher der JVM benötigt, um dies zu tun. Ein Angreifer mit so viel Zugriff kann Ihre Schlüsselereignisse abfangen, was diese völlig unbrauchbar macht (AFAIK, bitte korrigieren Sie mich, falls ich falsch liege).

Update

Dank der Kommentare muss ich meine Antwort aktualisieren. Offensichtlich gibt es zwei Fälle, in denen dies zu einer (sehr) geringfügigen Sicherheitsverbesserung führen kann, da dadurch die Zeit verkürzt wird, die ein Kennwort auf der Festplatte landen kann. Dennoch denke ich, dass es für die meisten Anwendungsfälle zu viel ist.

  • Ihr Zielsystem ist möglicherweise nicht richtig konfiguriert oder Sie müssen davon ausgehen, dass dies der Fall ist und Sie müssen paranoid über Core-Dumps sein (kann gültig sein, wenn die Systeme nicht von einem Administrator verwaltet werden). 
  • Ihre Software muss übermäßig paranoid sein, um Datenlecks zu verhindern, indem der Angreifer Zugriff auf die Hardware erhält - beispielsweise mit TrueCrypt (nicht fortgeführt), VeraCrypt oder CipherShed .

Wenn möglich, würde das Deaktivieren von Core-Dumps und der Auslagerungsdatei beide Probleme lösen. Sie würden jedoch Administratorrechte erfordern und möglicherweise die Funktionalität einschränken (weniger Arbeitsspeicher). Das Ziehen von RAM von einem laufenden System wäre nach wie vor ein berechtigtes Problem.

201
josefx
  1. Strings sind unveränderlich in Java: Wenn Sie das Kennwort als Klartext speichern, ist es im Speicher verfügbar, bis der Garbage Collector es löscht. Da Strings im String-Pool für die Wiederverwendbarkeit verwendet werden, besteht eine ziemlich hohe Wahrscheinlichkeit, dass es darin verbleibt Speicher für eine lange Zeit, die eine Sicherheitsbedrohung darstellt. Jeder, der Zugriff auf den Speicherabzug hat, kann das Kennwort im Klartext finden 
  2. Java-Empfehlung Verwendung der getPassword()-Methode von JPasswordField, die eine char [] - und eine veraltete getText()-Methode zurückgibt, die das Kennwort in Klartext mit Angabe des Sicherheitsgrunds zurückgibt. 
  3. toString () Es besteht immer die Gefahr, dass in der Protokolldatei oder in der Konsole Nur-Text gedruckt wird. Wenn Sie Array verwenden, wird jedoch nicht der Inhalt des Arrays gedruckt, sondern der Speicherort wird 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 die Verwendung von char [] nicht nur ausreicht, müssen Sie Inhalte löschen, um sicherer zu sein. Ich empfehle außerdem, mit gehashten oder verschlüsselten Kennwörtern anstelle von Klartext zu arbeiten und es aus dem Speicher zu löschen, sobald die Authentifizierung abgeschlossen ist.

131

Ich denke nicht, dass dies ein gültiger Vorschlag ist, aber ich kann den Grund zumindest vermuten.

Ich denke, die Motivation möchte sicherstellen, dass Sie alle Spuren des Kennworts im Speicher nach der Verwendung sofort und mit Sicherheit löschen können. Mit einem char[] können Sie jedes Element des Arrays mit einem Leerzeichen oder etwas sicherem überschreiben. Sie können den internen Wert eines String nicht auf diese Weise bearbeiten.

Aber das allein ist keine gute Antwort. Warum nicht einfach sicherstellen, dass ein Verweis auf den char[] oder String nicht entgeht? Dann gibt es keine Sicherheitslücke. Aber die Sache ist, dass String Objekte theoretisch intern()ed werden können und innerhalb des konstanten Pools am Leben erhalten werden. Ich denke, die Verwendung von char[] verbietet diese Möglichkeit.

76
Sean Owen

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 mit großer Sorgfalt darauf achten, Passwortzeichenfolgen überall durch char[] zu ersetzen (was natürlich eine gute Sache ist), scheinen andere sicherheitskritische Daten beim Löschen aus dem Speicher zu übersehen.

Ich denke an z. die PrivateKey Klasse. Stellen Sie sich ein Szenario vor, in dem Sie einen privaten RSA-Schlüssel aus einer PKCS # 12-Datei laden und diesen verwenden, um einen Vorgang auszuführen. In diesem Fall würde das Sniffing des Passworts allein nicht viel helfen, solange der physische Zugriff auf die Schlüsseldatei ordnungsgemäß eingeschränkt ist. Als Angreifer wäre es viel besser, wenn Sie den Schlüssel direkt anstelle des Kennworts erhalten. Die gewünschten Informationen können durchgesickert werden, Kerndumps, eine Debugger-Sitzung oder Auslagerungsdateien sind nur einige Beispiele.

Wie sich herausstellt, gibt es nichts, was die privaten Informationen einer PrivateKey aus dem Speicher löscht, weil es keine API gibt, mit der Sie die Bytes löschen können, aus denen die entsprechenden Informationen bestehen.

Dies ist eine schlimme Situation, da dieses Papier beschreibt, wie dieser Umstand möglicherweise ausgenutzt werden könnte.

Die OpenSSL-Bibliothek beispielsweise überschreibt kritische Speicherbereiche, bevor private Schlüssel freigegeben werden. Da Java nicht mehr vorhanden ist, benötigen wir explizite Methoden, um private Informationen für Java-Schlüssel zu löschen und für ungültig zu erklären, die unmittelbar nach der Verwendung des Schlüssels angewendet werden müssen. 

61
emboss

Wie Jon Skeet feststellt, gibt es keine andere Möglichkeit, als Reflexion zu verwenden. 

Wenn die Reflexion 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 laufen

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

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

Diese alte Kopie erscheint nicht in einem Heap-Dump, aber wenn Sie direkten Zugriff auf den Rohspeicher des Prozesses haben, können Sie sie sehen. Im Allgemeinen sollten Sie vermeiden, dass jemand solchen Zugriff hat.

46
Peter Lawrey

Dies sind alle Gründe, weshalb Sie ein char []-Array anstelle von String für ein Kennwort wählen sollten.

1. Da Strings in Java nicht veränderbar sind, wird das Kennwort im Klartext gespeichert, bis der Garbage-Collector es löscht. Da String zur Wiederverwendbarkeit im String-Pool verwendet wird, gibt es ein ziemlich hohe Chance, dass es lange in Erinnerung bleibt, was eine Sicherheitsbedrohung darstellt. 

Da jeder, der Zugriff auf den Speicherabzug hat, das Kennwort in Klartext finden kann, sollten Sie aus diesem Grund immer ein verschlüsseltes Kennwort anstelle von Nur-Text verwenden. Da Strings unveränderlich sind, kann der Inhalt von Strings nicht geändert werden, da bei jeder Änderung ein neuer String erzeugt wird. Wenn Sie ein char [] verwenden, können Sie dennoch alle Elemente als leer oder null festlegen. Durch das Speichern eines Passworts in einem Zeichen-Array wird das Sicherheitsrisiko des Diebstahls eines Passworts deutlich gemindert.

2. Java selbst empfiehlt die Verwendung der getPassword () - Methode von JPasswordField, die ein char [] zurückgibt, anstelle der veralteten getText () - Methode, die Kennwörter aus Sicherheitsgründen zurückgibt. Es ist gut, den Ratschlägen des Java-Teams zu folgen und sich an Standards zu halten, anstatt sich dagegen zu stellen.

3. Bei String besteht immer die Gefahr, dass in einer Protokolldatei oder Konsole reiner Text gedruckt wird. Wenn Sie jedoch ein Array verwenden, wird der Inhalt eines Arrays nicht gedruckt, sondern der Speicherort wird gedruckt. Obwohl es kein wirklicher Grund ist, macht es trotzdem Sinn.

String strPassword="Unknown";
char[] charPassword= new char[]{'U','n','k','w','o','n'};
System.out.println("String password: " + strPassword);
System.out.println("Character password: " + charPassword);

String password: Unknown
Character password: [[email protected]

Referenziert von diesem Blog ..__ Ich hoffe das hilft.

37
Human Being

Edit: Wenn ich nach einem Jahr der Sicherheitsforschung auf diese Antwort zurückkomme, ist mir klar, dass dies die ziemlich unglückliche Anspielung hat, dass Sie eigentlich nur Klartext-Passwörter vergleichen würden. Bitte nicht. Verwenden Sie einen sicheren Einweghash mit einem Salt und einer angemessenen Anzahl von Iterationen . Erwägen Sie die Verwendung einer Bibliothek: Dieses Zeug ist schwer zu finden!

Ursprüngliche Antwort: Was ist mit der Tatsache, dass String.equals () Kurzschlussbewertung verwendet und daher für einen Timing-Angriff anfällig ist? Es kann unwahrscheinlich sein, aber Sie könnten den Passwortvergleich theoretisch zeitlich festlegen, um die richtige Zeichenfolge zu bestimmen.

public boolean equals(Object anObject) {
    if (this == anObject) {
        return true;
    }
    if (anObject instanceof String) {
        String anotherString = (String)anObject;
        int n = value.length;
        // Quits here if Strings are different lengths.
        if (n == anotherString.value.length) {
            char v1[] = value;
            char v2[] = anotherString.value;
            int i = 0;
            // Quits here at first different character.
            while (n-- != 0) {
                if (v1[i] != v2[i])
                    return false;
                i++;
            }
            return true;
        }
    }
    return false;
}

Einige weitere Ressourcen zum Timing von Angriffen:

37
Graph Theory

Es gibt nichts, was char Array Ihnen gegen String gibt, wenn Sie es nach der Verwendung nicht manuell bereinigen, und ich habe niemanden gesehen, der das tatsächlich getan hat. Für mich ist also die Präferenz von char [] vs String etwas übertrieben.

Schauen Sie sich das an weit verbreitet Spring Security-Bibliothek hier und fragen Sie sich - sind Spring Security-Jungs inkompetent oder char [] -Kennwörter machen einfach keinen Sinn. Wenn ein böser Hacker Speicherauszüge von Ihrem RAM hat, stellen Sie sicher, dass er alle Passwörter erhält, selbst wenn Sie sie mithilfe ausgeklügelter Methoden ausblenden.

Java ändert sich jedoch ständig, und einige furchterregende Funktionen wie String Deduplication von Java 8 können interne String-Objekte ohne Ihr Wissen sein. Aber das ist ein anderes Gespräch.

33
Oleg Mikheev

Zeichenfolgen sind unveränderlich und können nach ihrer Erstellung nicht mehr geändert werden. Durch das Erstellen eines Kennworts als Zeichenfolge werden auf dem Heap oder im Zeichenfolge-Pool andere Verweise auf das Kennwort angezeigt. Wenn nun ein Heap-Dump des Java-Prozesses erstellt und sorgfältig durchsucht wird, kann er die Kennwörter möglicherweise erraten. Natürlich werden diese nicht benutzten Zeichenketten Müll gesammelt, aber das hängt davon ab, wann der GC startet.

Auf der anderen Seite sind char [] veränderbar, sobald die Authentifizierung abgeschlossen ist. Sie können sie mit einem beliebigen Zeichen wie allen Ms oder Backslashes überschreiben. Selbst wenn jemand einen Heap-Dump erstellt, kann er möglicherweise nicht die Passwörter erhalten, die derzeit nicht verwendet werden. Auf diese Weise haben Sie mehr Kontrolle in dem Sinne, als würden Sie den Objektinhalt selbst löschen oder warten, bis der GC dies tut.

29
Geek

1) Da Strings in Java unveränderlich sind, wenn Sie das Kennwort als Nur-Text speichern, ist es im Speicher verfügbar, bis der Garbage Collector es löscht. Da String im String-Pool für die Wiederverwendbarkeit verwendet wird, ist die Wahrscheinlichkeit hoch, dass er im Speicher verbleibt für die lange Dauer, die eine Sicherheitsbedrohung darstellt. Da jeder, der Zugriff auf den Speicherabzug hat, das Kennwort im Klartext finden kann, sollten Sie immer ein verschlüsseltes Kennwort als Klartext verwenden. Da Strings unveränderlich sind, gibt es keine Möglichkeit, den Inhalt von Strings zu ändern, da bei jeder Änderung ein neuer String erzeugt wird. Wenn Sie char [] verwenden, können Sie dennoch sein gesamtes Element als leer oder auf Null setzen. Das Speichern von Kennwörtern im Character Array verringert also das Sicherheitsrisiko, wenn das Kennwort gestohlen wird.

2) Java selbst empfiehlt die Verwendung der getPassword () - Methode von JPasswordField, die eine char [] - und die veraltete getText () - Methode zurückgibt, die das Kennwort im Klartext unter Angabe des Sicherheitsgrunds zurückgibt. Es ist gut, Ratschläge des Java-Teams zu befolgen und sich an den Standard zu halten, anstatt dagegen zu sein.

16
Neeraj Gahlawat

Die kurze und unkomplizierte Antwort wäre, weil char[] veränderlich ist, während String-Objekte dies nicht sind.

Strings in Java sind unveränderliche Objekte. Deshalb können sie nach der Erstellung nicht mehr geändert werden. Der einzige Weg, den Inhalt aus dem Speicher zu entfernen, ist das Sammeln von Müll. Nur wenn der vom Objekt freigegebene Speicher überschrieben werden kann, werden die Daten gelöscht.

Die Speicherbereinigung in Java erfolgt jetzt nicht in einem garantierten Intervall. Die Variable String kann daher für längere Zeit im Speicher verbleiben. Wenn ein Prozess während dieser Zeit abstürzt, kann der Inhalt der Zeichenfolge in einem Speicherabbild oder in einem Protokoll enden. 

Mit einem Zeichen-Array können Sie das Passwort lesen, die Bearbeitung so bald wie möglich beenden und den Inhalt sofort ändern.

16
Pritam Banerjee

String ist unveränderlich und geht in den String-Pool. Einmal geschrieben, kann es nicht überschrieben werden.

char[] ist ein Array, das Sie überschreiben sollten, wenn Sie das Kennwort verwendet haben. So sollte es gemacht werden:

char[] passw = request.getPassword().toCharArray()
if (comparePasswords(dbPassword, passw) {
 allowUser = true;
 cleanPassword(passw);
 cleanPassword(dbPassword);
 passw=null;
}

private static void cleanPassword (char[] pass) {
 for (char ch: pass) {
  ch = '0';
 }
}

Ein Szenario, in dem der Angreifer sie verwenden könnte, ist ein Crashdump. Wenn die JVM abstürzt und einen Speicherabzug generiert, können Sie das Kennwort sehen.

Das ist nicht unbedingt ein bösartiger Angreifer. Dies kann ein Support-Benutzer sein, der zu Überwachungszwecken auf den Server zugreifen kann. Er konnte in einen Absturzspeicher sehen und die Passwörter finden.

15
ACV

String in Java ist unveränderlich. Immer wenn eine Zeichenfolge erstellt wird, bleibt sie im Speicher, bis der Müll gesammelt wird. Jeder, der Zugriff auf den Speicher hat, kann also den Wert der Zeichenfolge lesen. 
Wenn der Wert der Zeichenfolge geändert wird, wird am Ende eine neue Zeichenfolge erstellt. So bleiben sowohl der ursprüngliche Wert als auch der geänderte Wert im Speicher, bis der Müll gesammelt wird. 

Mit dem Zeichen-Array kann der Inhalt des Arrays geändert oder gelöscht werden, sobald der Zweck des Passworts erfüllt ist. Der ursprüngliche Inhalt des Arrays wird nach seiner Änderung und sogar vor dem Beginn der Speicherbereinigung nicht im Speicher abgelegt.

Aus Sicherheitsgründen ist es besser, das Passwort als Zeichenarray zu speichern.

11
Saathvik

Die Verwendung des Passworts in String oder Char [] ist immer umstritten, da beide Ansätze ihre eigenen Vorteile und Nachteile hatten. Es hängt von der Anforderung ab, welcher Benutzer erwartet, erfüllt zu werden. Die folgenden Zeilen können hilfreich sein, um zu verstehen, wann welcher Container verwendet werden soll: Da String in Java unveränderlich ist, erstellt jedes Mal, wenn einige versuchen, Ihren String zu bearbeiten, ein neues Objekt und das vorhandene Objekt bleibt nicht temperiert. Dies kann als Vorteil für das Speichern von Passwörtern als String angesehen werden, aber andererseits. Das String-Objekt bleibt auch nach der Verwendung im Speicher. Wenn also jemand den Speicherort gefunden hat, kann das an diesem Ort gespeicherte Passwort leicht nachverfolgt werden. Wenn er zu Char [] wechselt, ist es veränderlich, hatte jedoch den Vorteil, dass der Programmierer nach der Verwendung das Array eindeutig reinigen oder Werte überschreiben kann. Damit es nach keinem Gebrauch gereinigt wird und niemand über die Informationen wissen kann, was Sie gespeichert haben.

Aufgrund der oben genannten Umstände kann man sich ein Bild davon machen, ob man String oder Char [] für ihre Anforderung braucht.

Vielen Dank.

1
Neeraj

Kurz gesagt, das liegt daran, was mit dem gespeicherten Passwort passieren kann, wenn Sie es nicht mehr benötigen. 

Ein String kann (möglicherweise) noch lange im Speicher verbleiben, nachdem Sie ihn nicht mehr verwendet haben. Ein Zeichen-Array kann jedoch geleert werden, sodass es keine Daten mehr enthält.

Das alles hat mit der Arbeitsweise der Müllsammler und unveränderlichen Objekte zu tun.

0