Saiten sind unveränderlich . Das bedeutet , dass , sobald Sie die erstellten String
, wenn ein anderer Prozess Speicherabbild kann, gibt es keine Möglichkeit (abgesehen von Reflexion ) Sie loszuwerden, die Daten , bevor bekommen können Garbage Collection in Kicks.
Mit einem Array können Sie die Daten explizit löschen, nachdem Sie damit fertig sind. Sie können das Array mit einem beliebigen Element überschreiben, und das Kennwort ist auch vor der Speicherbereinigung nirgendwo im System vorhanden.
Ja, dies ist ein Sicherheitsrisiko - aber selbst die Verwendung char[]
verringert nur das Zeitfenster für einen Angreifer und nur für diese bestimmte Art von Angriff.
Wie in den Kommentaren erwähnt, können Arrays, die vom Garbage Collector verschoben werden, Streukopien der Daten im Speicher belassen. Ich glaube, dies ist implementierungsspezifisch - der Garbage Collector löscht möglicherweise den gesamten Speicher, um dies zu vermeiden. Selbst wenn dies der Fall ist, gibt es immer noch die Zeit, in der das char[]
die tatsächlichen Charaktere als Angriffsfenster enthält.
char[]
Orten gespeicherten Passwörtern wird diese Angriffslinie abgeschnitten, was bei Verwendung nicht möglich istString
.Während andere Vorschläge hier gültig erscheinen, gibt es einen anderen guten Grund. Mit plain haben
String
Sie eine viel höhere Wahrscheinlichkeit, das Kennwort versehentlich auf Protokolle , Monitore oder einen anderen unsicheren Ort zu drucken .char[]
ist weniger anfällig.Bedenken Sie:
Drucke:
quelle
toString
istclassname@hashcode
.[C
stellt darchar[]
, der Rest ist hexadezimaler Hash-Code.Password
Klassentyp dafür schreiben . Es ist weniger dunkel und schwieriger, versehentlich irgendwohin zu gelangen.Um ein offizielles Dokument zu zitieren, der Java Cryptography Architecture Guide sagt über
char[]
vs.String
Passwörter (über Passwort-basierte Verschlüsselung, aber dies ist im Allgemeinen über Passwörter natürlich):Die Richtlinie 2-2 der Secure Coding Guidelines für die Java-Programmiersprache, Version 4.0, sagt ebenfalls etwas Ähnliches aus (obwohl dies ursprünglich im Zusammenhang mit der Protokollierung steht):
quelle
String
ist ziemlich gut verstanden und wie es sich in der JVM verhält ... es gibt gute Gründe,char[]
anstelle vonString
Passwörtern auf sichere Weise zu verwenden.At which point
- das ist anwendungsspezifisch, aber die allgemeine Regel lautet, dies zu tun, sobald Sie etwas erhalten, das ein Passwort sein soll (Klartext oder auf andere Weise). Sie erhalten es beispielsweise im Rahmen einer HTTP-Anfrage vom Browser. Sie können die Lieferung nicht steuern, aber Sie können Ihren eigenen Speicher steuern. Sobald Sie ihn erhalten, geben Sie ihn in ein Zeichen [] ein, tun Sie, was Sie damit tun müssen, setzen Sie dann alles auf '0' und lassen Sie den gc fordere es zurück.Zeichenarrays (
char[]
) können nach der Verwendung gelöscht werden, indem jedes Zeichen auf Null und Zeichenfolgen nicht gesetzt wird. Wenn jemand das Speicherbild irgendwie sehen kann, kann er ein Kennwort im Klartext sehen, wenn Zeichenfolgen verwendet werden. Wennchar[]
es jedoch verwendet wird, ist das Kennwort nach dem Löschen von Daten mit Nullen sicher.quelle
HttpServletRequest
im Klartext an das Objekt weiter. Wenn die JVM-Version 1.6 oder niedriger ist, befindet sie sich im Permgen-Raum. Wenn es in 1.7 ist, ist es noch lesbar, bis es gesammelt wird. (Wann immer das ist.)intern()
auf beliebige Zeichenfolgen zurückgreifen sollte . Aber Sie haben insofern Recht, als dieString
Instanzen an erster Stelle existieren (bis sie gesammelt werden), und wenn Sie sie anschließend inchar[]
Arrays umwandeln, ändert sich dies nicht.new String()
oderStringBuilder.toString()
ich verwaltete Anwendungen mit vielen String-Konstanten, und wir hatten als Ergebnis viel Permgen-Creep. Bis 1.7.intern()
einer beliebigen Zeichenfolge die Zuweisung einer äquivalenten Zeichenfolge im Permgenraum bewirken kann. Letzteres könnte GC'ed bekommen, wenn es keine wörtliche Zeichenfolge mit demselben Inhalt gäbe, die dieses Objekt teilt…Einige Leute glauben, dass Sie den Speicher, der zum Speichern des Passworts verwendet wird, überschreiben müssen, sobald Sie es nicht mehr benötigen. Dies verkürzt 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 benötigt, um den JVM-Speicher zu entführen, um dies zu tun. Ein Angreifer mit so viel Zugriff kann Ihre Schlüsselereignisse abfangen, was dies völlig unbrauchbar macht (AFAIK, bitte korrigieren Sie mich, wenn ich falsch liege).
Aktualisieren
Dank der Kommentare muss ich meine Antwort aktualisieren. Anscheinend 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 könnte. Trotzdem denke ich, dass es für die meisten Anwendungsfälle übertrieben ist.
Wenn möglich, würden beide Probleme durch Deaktivieren der Core-Dumps und der Auslagerungsdatei behoben. Sie würden jedoch Administratorrechte erfordern und könnten die Funktionalität reduzieren (weniger Arbeitsspeicher), und das Abrufen von RAM von einem laufenden System wäre weiterhin ein berechtigtes Anliegen.
quelle
Ich denke nicht, dass dies ein gültiger Vorschlag ist, aber ich kann zumindest den Grund erraten.
Ich denke, die Motivation besteht darin, sicherzustellen, dass Sie alle Spuren des Passworts im Speicher sofort und mit Sicherheit löschen können, nachdem es verwendet wurde. Mit a können
char[]
Sie jedes Element des Arrays mit einem Leerzeichen oder etwas sicherem überschreiben. Sie können den internen Wert von a aufString
diese Weise nicht bearbeiten .Aber das allein ist keine gute Antwort. warum nicht einfach sicherstellen, dass ein Verweis auf die
char[]
oderString
nicht entkommt? Dann gibt es kein Sicherheitsproblem. Aber die Sache ist, dassString
Objekteintern()
theoretisch bearbeitet und im konstanten Pool am Leben erhalten werden können. Ich nehme an, die Verwendungchar[]
verbietet diese Möglichkeit.quelle
char[]
sie geändert werden können, und dann ist es irrelevant, ob sie gesammelt werden oder nicht. Und da das Internieren von Zeichenfolgen explizit für Nicht-Literale durchgeführt werden muss, ist es dasselbe wie zu sagen, dass achar[]
durch ein statisches Feld referenziert werden kann.Die Antwort wurde bereits gegeben, aber ich möchte ein Problem, das ich kürzlich entdeckt habe, mit Java-Standardbibliotheken teilen. Während sie jetzt sehr darauf achten, Kennwortzeichenfolgen
char[]
überall zu ersetzen (was natürlich eine gute Sache ist), scheinen andere sicherheitskritische Daten beim Löschen aus dem Speicher übersehen zu werden.Ich denke zB an die PrivateKey- 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. In diesem Fall würde es Ihnen nicht viel helfen, nur das Kennwort 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 durchgesickert sein, Core-Dumps, eine Debugger-Sitzung oder Auslagerungsdateien sind nur einige Beispiele.
Und wie sich herausstellt, gibt es nichts, mit dem Sie die privaten Informationen
PrivateKey
aus dem Speicher löschen können, da es keine API gibt, mit der Sie die Bytes löschen können, die die entsprechenden Informationen bilden.Dies ist eine schlechte Situation, da in diesem Dokument beschrieben wird, wie dieser Umstand möglicherweise ausgenutzt werden kann.
Die OpenSSL-Bibliothek überschreibt beispielsweise kritische Speicherabschnitte, bevor private Schlüssel freigegeben werden. Da Java durch Müll gesammelt wird, benötigen wir explizite Methoden zum Löschen und Ungültigmachen privater Informationen für Java-Schlüssel, die unmittelbar nach Verwendung des Schlüssels angewendet werden sollen.
quelle
PrivateKey
, die ihren privaten Inhalt nicht tatsächlich in den Speicher lädt: beispielsweise über ein PKCS # 11-Hardware-Token. Möglicherweise könnte eine Software-Implementierung von PKCS # 11 die manuelle Bereinigung des Speichers übernehmen. VielleichtPKCS11
ist es besser , so etwas wie den NSS-Store zu verwenden (der den größten Teil seiner Implementierung mit dem Store-Typ in Java teilt ). DerKeychainStore
(OSX-Keystore) lädt den gesamten Inhalt des privaten Schlüssels in seinePrivateKey
Instanzen, sollte dies aber nicht müssen. (Nicht sicher, was derWINDOWS-MY
KeyStore unter Windows macht.)Wie Jon Skeet feststellt, gibt es keinen anderen Weg als die Verwendung von Reflexion.
Wenn Reflexion jedoch eine Option für Sie ist, können Sie dies tun.
wenn ausgeführt
Hinweis: Wenn das Zeichen [] des Strings 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 angezeigt, aber wenn Sie direkten Zugriff auf den Rohspeicher des Prozesses haben, können Sie ihn sehen. Im Allgemeinen sollten Sie vermeiden, dass jemand einen solchen Zugang hat.
quelle
'***********'
.Scanner
internen Puffer befindet und, da Sie es nicht verwendet habenSystem.console().readPassword()
, in lesbarer Form im Konsolenfenster angezeigt wird. In den meisten praktischen AnwendungsfällenusePassword
ist die Ausführungsdauer jedoch das eigentliche Problem. Wenn Sie beispielsweise eine Verbindung zu einem anderen Computer herstellen, dauert es sehr lange und teilt einem Angreifer mit, dass es der richtige Zeitpunkt ist, nach dem Kennwort im Heap zu suchen. Die einzige Lösung besteht darin, zu verhindern, dass Angreifer den Heapspeicher lesen…Dies sind alle Gründe, warum man für ein Passwort ein char [] Array anstelle von String wählen sollte .
1. Da Strings in Java unveränderlich sind, bleibt das Kennwort, wenn Sie es als einfachen Text speichern, im Speicher verfügbar, bis der Garbage Collector es löscht. Da String zur Wiederverwendbarkeit im String-Pool verwendet wird, besteht eine ziemlich hohe Wahrscheinlichkeit, dass dies der Fall ist bleiben lange im Gedächtnis, was eine Sicherheitsbedrohung darstellt.
Da jeder, der Zugriff auf den Speicherauszug hat, das Kennwort im Klartext finden kann, sollten Sie aus diesem Grund immer ein verschlüsseltes Kennwort anstelle von einfachem Text verwenden. Da Strings unveränderlich sind, kann der Inhalt von Strings auf keinen Fall geändert werden, da bei jeder Änderung ein neuer String erzeugt wird. Wenn Sie jedoch ein char [] verwenden, können Sie dennoch alle Elemente als leer oder null festlegen. Durch das Speichern eines Kennworts in einem Zeichenarray wird das Sicherheitsrisiko beim Diebstahl eines Kennworts deutlich verringert.
2. Java selbst empfiehlt die Verwendung der Methode getPassword () von JPasswordField, die ein char [] zurückgibt, anstelle der veralteten Methode getText (), die Kennwörter im Klartext unter Angabe von Sicherheitsgründen zurückgibt. Es ist gut, den Ratschlägen des Java-Teams zu folgen und sich an Standards zu halten, anstatt gegen diese zu verstoßen.
3. Bei String besteht immer das Risiko, dass einfacher Text in einer Protokolldatei oder Konsole gedruckt wird. Wenn Sie jedoch ein Array verwenden, wird der Inhalt eines Arrays nicht gedruckt, sondern der Speicherort wird gedruckt. Obwohl dies kein wirklicher Grund ist, macht es dennoch Sinn.
Referenziert von diesem Blog . Ich hoffe das hilft.
quelle
Bearbeiten: Wenn ich nach einem Jahr Sicherheitsforschung auf diese Antwort zurückkomme, stelle ich fest, dass dies die unglückliche Folgerung ist, dass Sie jemals Klartext-Passwörter vergleichen würden. Bitte nicht. Verwenden Sie einen sicheren Einweg-Hash mit einem Salz und einer angemessenen Anzahl von Iterationen . Erwägen Sie die Verwendung einer Bibliothek: Dieses Zeug ist schwer richtig zu machen!
Ursprüngliche Antwort: Was ist mit der Tatsache, dass String.equals () eine Kurzschlussauswertung verwendet und daher für einen Timing-Angriff anfällig ist? Es mag unwahrscheinlich sein, aber Sie könnten theoretisch den Passwortvergleich zeitlich festlegen, um die richtige Zeichenfolge zu bestimmen.
Weitere Ressourcen zum Timing von Angriffen:
quelle
Arrays.equals
fürchar[]
so hoch wie fürString.equals
. Wenn sich jemand darum kümmerte, gab es eine dedizierte Schlüsselklasse, die das eigentliche Passwort kapselte und sich um die Probleme kümmerte. Oh, warten Sie, die echten Sicherheitspakete haben dedizierte Schlüsselklassen. In diesen Fragen und Antworten geht es nur um eine Gewohnheit außerhalb von ihnen, z. B.JPasswordField
um die Verwendungchar[]
stattString
(wo die eigentlichen Algorithmenbyte[]
sowieso verwenden).sleep(secureRandom.nextInt())
sowieso etwas tun, bevor sie einen Anmeldeversuch ablehnt. Dies beseitigt nicht nur die Möglichkeit von Timing-Angriffen, sondern wirkt auch Brute-Force-Versuchen entgegen.Es gibt nichts, was Ihnen das char-Array gegen String gibt, es sei denn, Sie bereinigen es nach der Verwendung manuell, und ich habe noch niemanden gesehen, der das tatsächlich tut. Für mich ist die Präferenz von char [] gegenüber String etwas übertrieben.
Schauen Sie sich hier die weit verbreitete Spring Security-Bibliothek an und fragen Sie sich, ob Spring Security-Leute inkompetent sind oder char [] -Kennwörter einfach nicht viel Sinn machen. Wenn ein böser Hacker Speicherabbilder Ihres Arbeitsspeichers abruft, stellen Sie sicher, dass er alle Kennwörter erhält, auch wenn Sie sie auf ausgeklügelte Weise ausblenden.
Java ändert sich jedoch ständig, und einige beängstigende Funktionen wie die String-Deduplizierungsfunktion von Java 8 können String-Objekte ohne Ihr Wissen internieren. Aber das ist ein anderes Gespräch.
quelle
Zeichenfolgen sind unveränderlich und können nach ihrer Erstellung nicht mehr geändert werden. Wenn Sie ein Kennwort als Zeichenfolge erstellen, verbleiben streunende Verweise auf das Kennwort auf dem Heap oder im Zeichenfolgenpool. Wenn jemand einen Heap-Dump des Java-Prozesses erstellt und sorgfältig durchsucht, kann er möglicherweise die Kennwörter erraten. Natürlich werden diese nicht verwendeten Zeichenfolgen durch 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 Kennwörter abrufen, die derzeit nicht verwendet werden. Dies gibt Ihnen mehr Kontrolle in dem Sinne, dass Sie den Objektinhalt selbst löschen, anstatt darauf zu warten, dass der GC dies tut.
quelle
strings
UND derString
Pool (Pool zuvor verwendeter Strings) wurde BEIDE vor 1.7 in Permgen gespeichert. Siehe auch Abschnitt 5.1: docs.oracle.com/javase/specs/jvms/se6/html/… Die JVM hat immer überprüftStrings
, ob sie denselben Referenzwert haben, und hatString.intern()
FOR YOU aufgerufen . Das Ergebnis war, dass jedes Mal, wenn die JVM identische Strings imconstant_pool
oder -Haufen entdeckte, diese in das Permgen verschoben wurden. Und ich habe bis 1.7 an mehreren Anwendungen mit "Creeping Permgen" gearbeitet. Es war ein echtes Problem.constant_pool
IN-Permgen gelegt, und wenn ein String mehr als einmal verwendet wurde, wurde er interniert.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, sobald Sie das Kennwort verwendet haben. So sollte es gemacht werden:Ein Szenario, in dem der Angreifer es verwenden könnte, ist ein Absturzspeicherauszug. Wenn die JVM abstürzt und einen Speicherauszug generiert, wird das Kennwort angezeigt.
Das ist nicht unbedingt ein böswilliger externer Angreifer. Dies kann ein Support-Benutzer sein, der zu Überwachungszwecken Zugriff auf den Server hat. Er konnte in einen Crashdump schauen und die Passwörter finden.
quelle
request.getPassword()
erstellt der String nicht schon und fügt ihn dem Pool hinzu?ch = '0'
ändert die lokale Variablech
; es hat keine Auswirkung auf das Array. Und Ihr Beispiel ist sowieso sinnlos. Sie beginnen mit einer von Ihnen aufgerufenen ZeichenfolgeninstanztoCharArray()
, erstellen ein neues Array, und selbst wenn Sie das neue Array korrekt überschreiben, ändert es die Zeichenfolgeninstanz nicht, sodass es keinen Vorteil gegenüber der Verwendung der Zeichenfolge hat Beispiel.Arrays.fill(pass, '0');
Die kurze und unkomplizierte Antwort wäre, weil sie
char[]
veränderlich ist, währendString
Objekte dies nicht sind.Strings
in Java sind unveränderliche Objekte. Aus diesem Grund können sie nach ihrer Erstellung nicht mehr geändert werden. Daher besteht die einzige Möglichkeit, ihren Inhalt aus dem Speicher zu entfernen, darin, sie mit Müll zu sammeln. Erst dann kann der vom Objekt freigegebene Speicher überschrieben werden und die Daten gehen verloren.Jetzt erfolgt die Speicherbereinigung in Java nicht in einem garantierten Intervall. Das
String
kann somit für eine lange Zeit im Speicher verbleiben, und wenn ein Prozess während dieser Zeit abstürzt, kann der Inhalt der Zeichenfolge in einem Speicherauszug oder einem Protokoll enden.Mit einem Zeichenarray können Sie das Kennwort lesen, die Arbeit so schnell wie möglich beenden und dann sofort den Inhalt ändern.
quelle
String in Java ist unveränderlich. Wenn also eine Zeichenfolge erstellt wird, bleibt sie im Speicher, bis sie durch Müll gesammelt wird. Jeder, der Zugriff auf den Speicher hat, kann den Wert der Zeichenfolge lesen.
Wenn der Wert der Zeichenfolge geändert wird, wird 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 Zeichenarray kann der Inhalt des Arrays geändert oder gelöscht werden, sobald der Zweck des Kennworts erfüllt ist. Der ursprüngliche Inhalt des Arrays wird nach dem Ändern und noch vor dem Start der Speicherbereinigung nicht im Speicher gefunden.
Aus Sicherheitsgründen ist es besser, das Kennwort als Zeichenarray zu speichern.
quelle
Es ist fraglich, ob Sie String oder Char [] für diesen Zweck verwenden sollten, da beide ihre Vor- und Nachteile haben. Dies hängt davon ab, was der Benutzer benötigt.
Da Strings in Java unveränderlich sind, wird bei jedem Versuch, Ihren String zu manipulieren, ein neues Objekt erstellt, und der vorhandene String bleibt davon unberührt. Dies könnte als Vorteil für das Speichern eines Kennworts als Zeichenfolge angesehen werden, das Objekt bleibt jedoch auch nach der Verwendung im Speicher. Wenn also jemand irgendwie den Speicherort des Objekts hat, kann diese Person Ihr an diesem Speicherort gespeichertes Passwort leicht nachverfolgen.
Char [] ist veränderlich, hat jedoch den Vorteil, dass der Programmierer nach seiner Verwendung das Array explizit bereinigen oder Werte überschreiben kann. Wenn es fertig ist, wird es gereinigt und niemand kann jemals etwas über die Informationen erfahren, die Sie gespeichert haben.
Basierend auf den oben genannten Umständen kann man sich ein Bild davon machen, ob man für seine Anforderungen mit String oder mit Char [] arbeiten soll.
quelle
Fallzeichenfolge:
Fall CHAR ARRAY:
quelle