Frage Wie generiert man eine zufällige alphanumerische Zeichenkette?


Ich habe nach einem gesucht einfach Java-Algorithmus zum Generieren einer pseudozufälligen alphanumerischen Zeichenfolge. In meiner Situation würde es als eine einzigartige Sitzung / Schlüssel-Identifikator verwendet werden, der "wahrscheinlich" einzigartig sein würde über 500K + Generation (meine Bedürfnisse erfordern wirklich nichts, das viel ausgeklügelter ist). Im Idealfall könnte ich eine Länge angeben, die von meinen Eindeutigkeitsanforderungen abhängt. Beispielsweise könnte eine generierte Zeichenfolge der Länge 12 ungefähr so ​​aussehen "AEYGF7K0DM1X".


1458


Ursprung


Antworten:


Algorithmus

Um eine zufällige Zeichenfolge zu generieren, verketten Sie zufällig gezogene Zeichen aus der Menge der zulässigen Symbole, bis die Zeichenfolge die gewünschte Länge erreicht.

Implementierung

Hier ist ein ziemlich einfacher und sehr flexibler Code zum Generieren von zufälligen Bezeichnern. Lesen Sie die folgenden Informationen für wichtige Anwendungshinweise.

import java.security.SecureRandom;
import java.util.Locale;
import java.util.Objects;
import java.util.Random;

public class RandomString {

    /**
     * Generate a random string.
     */
    public String nextString() {
        for (int idx = 0; idx < buf.length; ++idx)
            buf[idx] = symbols[random.nextInt(symbols.length)];
        return new String(buf);
    }

    public static final String upper = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";

    public static final String lower = upper.toLowerCase(Locale.ROOT);

    public static final String digits = "0123456789";

    public static final String alphanum = upper + lower + digits;

    private final Random random;

    private final char[] symbols;

    private final char[] buf;

    public RandomString(int length, Random random, String symbols) {
        if (length < 1) throw new IllegalArgumentException();
        if (symbols.length() < 2) throw new IllegalArgumentException();
        this.random = Objects.requireNonNull(random);
        this.symbols = symbols.toCharArray();
        this.buf = new char[length];
    }

    /**
     * Create an alphanumeric string generator.
     */
    public RandomString(int length, Random random) {
        this(length, random, alphanum);
    }

    /**
     * Create an alphanumeric strings from a secure generator.
     */
    public RandomString(int length) {
        this(length, new SecureRandom());
    }

    /**
     * Create session identifiers.
     */
    public RandomString() {
        this(21);
    }

}

Anwendungsbeispiele

Erstellen Sie einen unsicheren Generator für 8-stellige Bezeichner:

RandomString gen = new RandomString(8, ThreadLocalRandom.current());

Erstellen Sie einen sicheren Generator für Sitzungskennungen:

RandomString session = new RandomString();

Erstellen Sie einen Generator mit leicht lesbaren Codes zum Drucken. Die Zeichenfolgen sind länger als vollständige alphanumerische Zeichenfolgen, um die Verwendung weniger Symbole zu kompensieren:

String easy = RandomString.digits + "ACEFGHJKLMNPQRUVWXYabcdefhijkprstuvwx";
RandomString tickets = new RandomString(23, new SecureRandom(), easy);

Verwenden Sie sie als Sitzungs-IDs

Das Generieren von Sitzungskennungen, die wahrscheinlich eindeutig sind, ist nicht gut genug, oder Sie können einfach einen einfachen Zähler verwenden. Angreifer entführen Sitzungen, wenn vorhersehbare Identifikatoren verwendet werden.

Es besteht eine Spannung zwischen Länge und Sicherheit. Kürzere Bezeichner sind einfacher zu erraten, da es weniger Möglichkeiten gibt. Längere Kennungen verbrauchen jedoch mehr Speicher und Bandbreite. Ein größerer Satz von Symbolen hilft, kann jedoch zu Codierungsproblemen führen, wenn IDs in URLs enthalten sind oder manuell neu eingegeben werden.

Die zugrunde liegende Quelle der Zufälligkeit oder Entropie für Sitzungsidentifikatoren sollte von einem Zufallszahlengenerator stammen, der für die Kryptographie ausgelegt ist. Das Initialisieren dieser Generatoren kann jedoch manchmal rechenintensiv oder langsam sein, daher sollte versucht werden, sie nach Möglichkeit wieder zu verwenden.

Verwenden Sie als Objektbezeichner

Nicht jede Anwendung erfordert Sicherheit. Die zufällige Zuweisung kann eine effiziente Möglichkeit für mehrere Entitäten sein, Identifikatoren in einem gemeinsam genutzten Raum zu generieren, ohne dass eine Koordination oder Partitionierung erforderlich ist. Die Koordination kann langsam sein, insbesondere in einer Cluster- oder verteilten Umgebung, und das Aufteilen eines Bereichs verursacht Probleme, wenn Entitäten mit zu kleinen oder zu großen Freigaben enden.

Bezeichner, die generiert werden, ohne Maßnahmen zu ergreifen, um sie unvorhersehbar zu machen, sollten auf andere Weise geschützt werden, wenn ein Angreifer sie möglicherweise anzeigen und manipulieren kann, wie es bei den meisten Webanwendungen der Fall ist. Es sollte ein separates Autorisierungssystem geben, das Objekte schützt, deren Kennung von einem Angreifer ohne Zugriffsberechtigung erraten werden kann.

Es muss auch darauf geachtet werden, Kennungen zu verwenden, die lang genug sind, um Kollisionen angesichts der erwarteten Gesamtzahl von Kennungen unwahrscheinlich zu machen. Dies wird als "das Geburtstagsparadoxon" bezeichnet. Die Wahrscheinlichkeit einer Kollision,  p, ist ungefähr n2/ (2qx), woher n ist die Anzahl der tatsächlich erzeugten Kennungen, q ist die Anzahl der verschiedenen Symbole im Alphabet und x ist die Länge der Kennungen. Dies sollte eine sehr kleine Zahl sein, wie 2-50 oder weniger.

Das Ausarbeiten zeigt, dass die Wahrscheinlichkeit einer Kollision unter 500k 15-stelligen Bezeichnern etwa 2 ist-52, was wahrscheinlich weniger wahrscheinlich ist als unerkannte Fehler von kosmischen Strahlen usw.

Vergleich mit UUIDs

Gemäß ihrer Spezifikation sind UUIDs nicht so ausgelegt, dass sie unvorhersehbar sind, und sollte nicht als Sitzungskennungen verwendet werden.

UUIDs in ihrem Standardformat benötigen viel Platz: 36 Zeichen für nur 122 Bit Entropie. (Nicht alle Bits einer "zufälligen" UUID werden zufällig ausgewählt.) Eine zufällig ausgewählte alphanumerische Zeichenfolge enthält mehr Entropie in nur 21 Zeichen.

UUIDs sind nicht flexibel; Sie haben eine standardisierte Struktur und Layout. Dies ist ihre Haupttugend sowie ihre Hauptschwäche. Bei der Zusammenarbeit mit einer externen Partei kann die von UUIDs angebotene Standardisierung hilfreich sein. Für den rein internen Gebrauch können sie ineffizient sein.


1396



Java bietet eine Möglichkeit, dies direkt zu tun. Wenn Sie die Bindestriche nicht möchten, sind sie leicht zu entfernen. Benutz einfach uuid.replace("-", "")

import java.util.UUID;

public class randomStringGenerator {
    public static void main(String[] args) {
        System.out.println(generateString());
    }

    public static String generateString() {
        String uuid = UUID.randomUUID().toString();
        return "uuid = " + uuid;
    }
}

Ausgabe:

uuid = 2d7428a6-b58c-4008-8575-f05549f16316

732



static final String AB = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
static SecureRandom rnd = new SecureRandom();

String randomString( int len ){
   StringBuilder sb = new StringBuilder( len );
   for( int i = 0; i < len; i++ ) 
      sb.append( AB.charAt( rnd.nextInt(AB.length()) ) );
   return sb.toString();
}

468



Wenn Sie gerne Apache-Klassen verwenden, könnten Sie verwenden org.apache.commons.text.RandomStringGenerator (commons-Text).

Beispiel:

RandomStringGenerator randomStringGenerator =
        new RandomStringGenerator.Builder()
                .withinRange('0', 'z')
                .filteredBy(CharacterPredicates.LETTERS, CharacterPredicates.DIGITS)
                .build();
randomStringGenerator.generate(12); // toUpperCase() if you want

Seit commons-lang 3.6, RandomStringUtils ist veraltet.


453



In einer Zeile:

Long.toHexString(Double.doubleToLongBits(Math.random()));

http://mynotes.wordpress.com/2009/07/23/java-generating-random-string/


90



Sie können dafür die Apache-Bibliothek verwenden: RandomStringUtils

RandomStringUtils.randomAlphanumeric(20).toUpperCase();

87



verwenden Dollar sollte einfach sein als:

// "0123456789" + "ABCDE...Z"
String validCharacters = $('0', '9').join() + $('A', 'Z').join();

String randomString(int length) {
    return $(validCharacters).shuffle().slice(length).toString();
}

@Test
public void buildFiveRandomStrings() {
    for (int i : $(5)) {
        System.out.println(randomString(12));
    }
}

Es gibt so etwas aus:

DKL1SBH9UJWC
JH7P0IT21EA5
5DTI72EO6SFU
HQUMJTEBNF7Y
1HCR6SKYWGT7

36



Dies ist ohne externe Bibliotheken leicht zu erreichen.

1. Kryptographische Pseudozufallsdatenerzeugung

Zuerst benötigen Sie eine kryptografische PRNG. Java hat SecureRandom dafür verwendet typischerweise die beste Entropiequelle auf der Maschine (z.B. /dev/random). Lesen Sie hier mehr.

SecureRandom rnd = new SecureRandom();
byte[] token = new byte[byteLength];
rnd.nextBytes(token);

Hinweis:  SecureRandom ist die langsamste, aber sicherste Methode in Java, zufällige Bytes zu generieren. Ich empfehle jedoch, die Leistung NICHT zu berücksichtigen, da sie normalerweise keinen wirklichen Einfluss auf Ihre Anwendung hat, es sei denn, Sie müssen Millionen von Token pro Sekunde generieren.

2. Erforderlicher Platz möglicher Werte

Als nächstes müssen Sie entscheiden, wie einzigartig Ihr Token sein soll. Der einzige Punkt, an dem die Entropie berücksichtigt wird, ist, dass das System Brute-Force-Angriffen widerstehen kann: Der Raum möglicher Werte muss so groß sein, dass jeder Angreifer nur einen vernachlässigbaren Anteil der Werte in nicht-lächerlicher Zeit ausprobieren kann1. Eindeutige Bezeichner wie zum Beispiel "zufällig" UUID haben 122bit Entropie (dh. 2 ^ 122 = 5,3x10 ^ 36) - die Chance der Kollision ist "* (...) da es eine Eins zu einer Milliarde Chance der Duplizierung gibt, müssen 103 Billionen Version 4 UUIDs erzeugt werden2". Wir werden 128 Bit wählen, da es genau in 16 Bytes passt und wird als gesehen sehr ausreichend weil es für praktisch alle, aber die extremsten Anwendungsfälle einzigartig ist und man nicht über Duplikate nachdenken muss. Hier ist eine einfache Vergleichstabelle der Entropie einschließlich einfacher Analyse der Geburtstagsproblem.

comparison of token sizes

Für einfache Anforderungen reichen 8 oder 12 Byte Länge aus, aber mit 16 Byte sind Sie auf der "sicheren Seite".

Und das ist es im Grunde genommen. Die letzte Sache ist, über die Codierung nachzudenken, so dass sie als druckbarer Text dargestellt werden kann (lesen, a String).

3. Binär zu Text-Codierung

Typische Kodierungen umfassen:

  • Base64 Jedes Zeichen codiert 6 Bit und erzeugt einen Overhead von 33%. Leider gibt es im JDK keine Standardimplementierung (7 und darunter - da ist es Android und Java 8+). Aber zahlreiche Bibliotheken existieren das füge das hinzu. Der Nachteil ist, dass Standard Base64 ist nicht sicher für zB. URLs und als Dateiname in den meisten Dateisystemen, die eine zusätzliche Codierung erfordern (z.B. URL-Codierung) oder der Die URL-sichere Version von Base64 wird verwendet. Beispiel Codierung 16 Bytes mit Auffüllung: XfJhfv3C0P6ag7y9VQxSbw==

  • Base32 Jedes Zeichen codiert 5 Bit und erzeugt einen Overhead von 40%. Dies wird verwendet A-Z und 2-7 so dass es einigermaßen Platz sparend ist, während es alphanumerisch nicht zwischen Groß- und Kleinschreibung unterscheidet. Es gibt kein Standardimplementierung im JDK. Beispiel für die Codierung von 16 Byte ohne Auffüllung: WUPIL5DQTZGMF4D3NX5L7LNFOY

  • Base16 (hex) Jedes Zeichen codiert 4 Bit, wobei 2 Zeichen pro Byte erforderlich sind (dh 16 Byte erzeugen eine Zeichenfolge der Länge 32). Daher ist hex weniger Platz sparend als Base32 aber es ist sicher in den meisten Fällen (URL) zu verwenden, da es nur verwendet 0-9 und A zu F. Beispielkodierung 16 Bytes: 4fa3dd0f57cb3bf331441ed285b27735. Siehe eine SO-Diskussion über das Konvertieren in hex.

Zusätzliche Codierungen wie Basis85 und das Exotische Basis122 existieren mit besser / schlechter Raumeffizienz. 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. Sehen mehr Kodierungsschemata im Wikipedia-Artikel.

4. Zusammenfassung und Beispiel

  • Benutzen SecureRandom
  • Verwenden Sie mindestens 16 Byte (2 ^ 128) möglicher Werte
  • Kodieren Sie entsprechend Ihren Anforderungen (normalerweise hex oder base32 wenn Sie es brauchen, um alphanumerisch zu sein)

Nicht

  • ... verwenden Sie Ihre Hausgebräu-Codierung: besser wartbar und für andere lesbar, wenn sie sehen, welche Standardcodierung Sie anstelle von seltsamen for-Schleifen verwenden, die Zeichen gleichzeitig erstellen.
  • ... benutze UUID: Sie verschwenden 6 Bits Entropie und haben eine ausführliche String-Darstellung

Beispiel: Hex-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); //hex encoding
}

//generateRandomHexToken(16) -> 2189df7475e96aa3982dbeab266497cd

Beispiel: Werkzeug

Wenn Sie ein gebrauchsfertiges CLI-Tool haben möchten, können Sie Würfel verwenden: https://github.com/patrickfav/dice


32