Algoritmo
Per generare una stringa casuale, concatena i caratteri disegnati in modo casuale dall'insieme di simboli accettabili fino a quando la stringa raggiunge la lunghezza desiderata.
Implementazione
Ecco un codice abbastanza semplice e molto flessibile per generare identificatori casuali. Leggi le informazioni che seguono per importanti note sull'applicazione.
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);
}
}
Esempi di utilizzo
Crea un generatore non sicuro per identificatori di 8 caratteri:
RandomString gen = new RandomString(8, ThreadLocalRandom.current());
Crea un generatore sicuro per identificatori di sessione:
RandomString session = new RandomString();
Crea un generatore con codici di facile lettura per la stampa. Le stringhe sono più lunghe delle stringhe alfanumeriche complete per compensare l'utilizzo di un minor numero di simboli:
String easy = RandomString.digits + "ACEFGHJKLMNPQRUVWXYabcdefhijkprstuvwx";
RandomString tickets = new RandomString(23, new SecureRandom(), easy);
Utilizzare come identificatori di sessione
La generazione di identificativi di sessione che sono probabilmente univoci non è abbastanza buona, oppure potresti semplicemente usare un semplice contatore. Gli attaccanti dirottano le sessioni quando vengono utilizzati identificatori prevedibili.
C'è tensione tra lunghezza e sicurezza. Gli identificatori più brevi sono più facili da indovinare, perché ci sono meno possibilità. Ma gli identificatori più lunghi consumano più memoria e larghezza di banda. Un set più grande di simboli aiuta, ma potrebbe causare problemi di codifica se gli identificatori sono inclusi negli URL o reinseriti a mano.
La fonte sottostante di casualità, o entropia, per identificatori di sessione dovrebbe provenire da un generatore di numeri casuali progettato per la crittografia. Tuttavia, l'inizializzazione di questi generatori a volte può essere computazionalmente costosa o lenta, quindi è necessario fare uno sforzo per riutilizzarli quando possibile.
Utilizzare come identificatori di oggetti
Non tutte le applicazioni richiedono sicurezza. L'assegnazione casuale può essere un modo efficiente per più entità di generare identificatori in uno spazio condiviso senza alcun coordinamento o partizionamento. Il coordinamento può essere lento, specialmente in un ambiente cluster o distribuito, e la divisione di uno spazio causa problemi quando le entità finiscono con condivisioni troppo piccole o troppo grandi.
Gli identificatori generati senza prendere misure per renderli imprevedibili dovrebbero essere protetti con altri mezzi se un attaccante potrebbe essere in grado di visualizzarli e manipolarli, come accade nella maggior parte delle applicazioni web. Dovrebbe esistere un sistema di autorizzazione separato che protegga gli oggetti il cui identificatore può essere indovinato da un utente malintenzionato senza autorizzazione di accesso.
Occorre inoltre prestare attenzione all'utilizzo di identificatori sufficientemente lunghi da rendere improbabili le collisioni, dato il numero totale previsto di identificatori. Questo è indicato come "il paradosso del compleanno". La probabilità di una collisione, p , è approssimativamente n 2 / (2q x ), dove n è il numero di identificatori effettivamente generati, q è il numero di simboli distinti nell'alfabeto e x è la lunghezza degli identificatori. Questo dovrebbe essere un numero molto piccolo, come 2-50 o meno.
Risolvendo questo problema, la probabilità di collisione tra identificatori di 500k di 15 caratteri è di circa 2 -52 , il che è probabilmente meno probabile degli errori non rilevati dai raggi cosmici, ecc.
Confronto con gli UUID
Secondo le loro specifiche, gli UUID non sono progettati per essere imprevedibili e non dovrebbero essere usati come identificatori di sessione.
Gli UUID nel loro formato standard occupano molto spazio: 36 caratteri per solo 122 bit di entropia. (Non tutti i bit di un UUID "casuale" sono selezionati in modo casuale.) Una stringa alfanumerica scelta casualmente racchiude più entropia in soli 21 caratteri.
Gli UUID non sono flessibili; hanno una struttura e un layout standardizzati. Questa è la loro principale virtù, nonché la loro principale debolezza. Quando si collabora con una parte esterna, la standardizzazione offerta dagli UUID può essere utile. Per un uso puramente interno, possono essere inefficienti.