Dies ist ohne externe Bibliotheken leicht zu bewerkstelligen.
1. Kryptographische Pseudo-Zufallsdatengenerierung (PRNG)
Zunächst benötigen Sie einen kryptografischen PRNG. Java hat SecureRandom
und verwendet normalerweise die beste Entropiequelle auf dem Rechner (z. B. /dev/random
). Lesen Sie hier mehr .
SecureRandom rnd = new SecureRandom();
byte[] token = new byte[byteLength];
rnd.nextBytes(token);
Note : SecureRandom
ist die langsamste, aber sicherste Methode in Java, um Zufallsbytes zu erzeugen. Ich empfehle jedoch no Die Leistung sollte hier nicht berücksichtigt werden, da sie normalerweise keine wirklichen Auswirkungen auf Ihre Anwendung hat, es sei denn, Sie müssen Millionen von Token pro Sekunde erzeugen.
2. Erforderlicher Raum der möglichen Werte
Als Nächstes müssen Sie entscheiden, "wie einzigartig" Ihr Token sein soll. Der Sinn und Zweck der Betrachtung der Entropie ist es, sicherzustellen, dass das System Brute-Force-Angriffen widerstehen kann: Der Raum möglicher Werte muss so groß sein, dass ein Angreifer nur einen vernachlässigbaren Anteil der Werte in nicht unvorstellbarer Zeit ausprobieren kann 1 .
Eindeutige Identifikatoren wie zufällige UUID
haben 122 Bit Entropie (d. h. 2^122 = 5,3x10^36) - die Wahrscheinlichkeit einer Kollision ist "*(...) damit die Wahrscheinlichkeit einer Vervielfältigung 1:1 Milliarde beträgt, müssen 103 Billionen UUIDs der Version 4 erzeugt werden 2 ". Wir wählen 128 Bit, da dies genau in 16 Bytes passt. und wird gesehen als völlig ausreichend für die Eindeutigkeit in praktisch allen, außer den extremsten, Anwendungsfällen und man muss sich keine Gedanken über Duplikate machen. Hier ist eine einfache Vergleichstabelle der Entropie einschließlich einer einfachen Analyse der Geburtstagsproblem .
Für einfache Anforderungen können 8 oder 12 Byte Länge ausreichen, aber mit 16 Byte ist man auf der "sicheren Seite".
Und das war's im Grunde auch schon. Als Letztes muss man sich Gedanken über die Kodierung machen, damit der Text als druckbarer Text dargestellt werden kann (lesen Sie, ein String
).
3. Binär-zu-Text-Kodierung
Typische Kodierungen sind:
-
Base64
Jedes Zeichen wird mit 6 Bit kodiert, was einen Overhead von 33 % bedeutet. Glücklicherweise gibt es Standardimplementierungen in Java 8+ y Android . Mit älterem Java können Sie jede der zahlreiche Bibliotheken von Drittanbietern . Wenn Sie möchten, dass Ihre Token URL-sicher sind, verwenden Sie die URL-sicher Version von RFC4648 (die normalerweise von den meisten Implementierungen unterstützt wird). Beispiel für die Kodierung von 16 Bytes mit Auffüllung: XfJhfv3C0P6ag7y9VQxSbw==
-
Base32
Jedes Zeichen wird mit 5 Bit kodiert, was einen Overhead von 40 % bedeutet. Dies verwendet A-Z
y 2-7
Dadurch ist sie relativ platzsparend und unterscheidet nicht zwischen Groß- und Kleinschreibung und ist alphanumerisch. Es gibt keine Standardimplementierung im JDK . Beispiel für die Codierung von 16 Bytes ohne Auffüllen: WUPIL5DQTZGMF4D3NX5L7LNFOY
-
Base16
(hexadezimal) kodiert jedes Zeichen vier Bit, was zwei Zeichen pro Byte erfordert (d.h. 16 Bytes ergeben eine Zeichenkette der Länge 32). Daher ist die Hexadezimalkodierung weniger platzsparend als Base32
aber es ist in den meisten Fällen sicher zu verwenden (URL), da es nur 0-9
y A
a F
. Beispiel Kodierung 16 Bytes: 4fa3dd0f57cb3bf331441ed285b27735
. Siehe eine Stack Overflow-Diskussion über die Konvertierung in Hexadezimal hier .
Zusätzliche Kodierungen wie Basis85 und das Exotische Basis122 mit besserer/schlechterer Flächeneffizienz existieren. Sie können Ihre eigene Kodierung erstellen (was im Grunde die meisten Antworten in diesem Thread tun), aber ich würde davon abraten, wenn Sie nicht sehr spezifische Anforderungen haben. Siehe weitere Kodierungsverfahren im Wikipedia-Artikel .
4. Zusammenfassung und Beispiel
- Utilice
SecureRandom
- Mindestens 16 Bytes (2^128) möglicher Werte verwenden
- Codieren Sie entsprechend Ihren Anforderungen (normalerweise
hex
o base32
wenn es alphanumerisch sein soll)
Nicht
- ... verwenden Sie Ihre selbst gebraute Kodierung: _besser pflegbar und lesbar für andere, wenn sie sehen, welche Standardkodierung Sie verwenden, anstatt seltsame für Schleifen, in denen Zeichen auf einmal erstellt werden._
- ... UUID verwenden: es gibt keine Garantien für die Zufälligkeit; Sie verschwenden 6 Bits Entropie und haben eine ausführliche String-Darstellung
Beispiel: Hexadezimaler Token-Generator
public static String generateRandomHexToken(int byteLength) {
SecureRandom secureRandom = new SecureRandom();
byte[] token = new byte[byteLength];
secureRandom.nextBytes(token);
return new BigInteger(1, token).toString(16); // Hexadecimal encoding
}
//generateRandomHexToken(16) -> 2189df7475e96aa3982dbeab266497cd
Beispiel: Base64-Token-Generator (URL-sicher)
public static String generateRandomBase64Token(int byteLength) {
SecureRandom secureRandom = new SecureRandom();
byte[] token = new byte[byteLength];
secureRandom.nextBytes(token);
return Base64.getUrlEncoder().withoutPadding().encodeToString(token); //base64 encoding
}
//generateRandomBase64Token(16) -> EEcCCAYuUcQk7IuzdaPzrg
Beispiel: Java CLI-Werkzeug
Wenn Sie ein einsatzbereites CLI-Tool wünschen, können Sie Folgendes verwenden Würfel :
Beispiel: Verwandtes Thema - Schützen Sie Ihre aktuellen Ids
Wenn Sie bereits eine ID haben, die Sie verwenden können (z. B. eine synthetische long
in Ihrer Entität), sondern den internen Wert nicht veröffentlichen wollen können Sie diese Bibliothek verwenden, um sie zu verschlüsseln und unkenntlich zu machen: https://github.com/patrickfav/id-mask
IdMask<Long> idMask = IdMasks.forLongIds(Config.builder(key).build());
String maskedId = idMask.mask(id);
// Example: NPSBolhMyabUBdTyanrbqT8
long originalId = idMask.unmask(maskedId);
166 Stimmen
Vorsicht das Geburtstagsparadoxon .
64 Stimmen
Selbst wenn man das Geburtstagsparadoxon in Betracht zieht, bräuchte man bei Verwendung von 12 alphanumerischen Zeichen (62 insgesamt) immer noch weit über 34 Milliarden Zeichenfolgen, um das Paradoxon zu erreichen. Und das Geburtstagsparadoxon garantiert sowieso keine Kollision, es sagt nur, dass die Wahrscheinlichkeit über 50 % liegt.
6 Stimmen
@NullUserException 50 % Erfolgschance (pro Versuch) ist verdammt hoch: Selbst bei 10 Versuchen liegt die Erfolgsquote bei 0,999. Wenn man das und die Tatsache bedenkt, dass man innerhalb von 24 Stunden VIELE Versuche machen kann, braucht man keine 34 Milliarden Zeichenfolgen, um ziemlich sicher zu sein, dass man mindestens eine davon errät. Das ist der Grund, warum einige Sitzungs-Tokens sehr, sehr lang sein sollten.
0 Stimmen
Dieser Blogpost sollte nützlich sein - Code zur Erzeugung alphanumerischer Zeichenfolgen: rationaljava.com/2015/06/
20 Stimmen
Diese 3 einzeiligen Codes sind sehr nützlich, schätze ich.
Long.toHexString(Double.doubleToLongBits(Math.random()));
UUID.randomUUID().toString();
RandomStringUtils.randomAlphanumeric(12);
26 Stimmen
@Pijusn Ich weiß, das ist alt, aber... die "50% Chance" im Geburtstagsparadoxon ist NO "pro Versuch", das heißt "50 % Wahrscheinlichkeit, dass von (in diesem Fall) 34 Milliarden Zeichenfolgen mindestens ein Paar Dubletten vorhanden ist". Sie bräuchten 1,6 sept illionen - 1,6e21 - Einträge in Ihrer Datenbank, um eine Chance von 50% pro Versuch zu haben.
0 Stimmen
Es ist besser, die
"-"
in der Zeichenkette0 Stimmen
Zufällige Zeichenkette mit Javascript, Java, Python, Rust, Bash