Algoritmo
Para gerar uma sequência aleatória, concatene caracteres desenhados aleatoriamente a partir do conjunto de símbolos aceitáveis até que a sequência atinja o comprimento desejado.
Implementação
Aqui está um código bastante simples e muito flexível para gerar identificadores aleatórios. Leia as informações a seguir para obter importantes notas de aplicação.
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);
}
}
Exemplos de uso
Crie um gerador inseguro para identificadores de 8 caracteres:
RandomString gen = new RandomString(8, ThreadLocalRandom.current());
Crie um gerador seguro para identificadores de sessão:
RandomString session = new RandomString();
Crie um gerador com códigos fáceis de ler para impressão. As cadeias são mais longas que as alfanuméricas completas para compensar o uso de menos símbolos:
String easy = RandomString.digits + "ACEFGHJKLMNPQRUVWXYabcdefhijkprstuvwx";
RandomString tickets = new RandomString(23, new SecureRandom(), easy);
Use como identificadores de sessão
Gerar identificadores de sessão que provavelmente são únicos não é bom o suficiente, ou você pode simplesmente usar um contador simples. Os invasores sequestram sessões quando identificadores previsíveis são usados.
Há tensão entre comprimento e segurança. Identificadores mais curtos são mais fáceis de adivinhar, porque há menos possibilidades. Mas identificadores mais longos consomem mais armazenamento e largura de banda. Um conjunto maior de símbolos ajuda, mas pode causar problemas de codificação se os identificadores forem incluídos nos URLs ou digitados novamente manualmente.
A fonte subjacente de aleatoriedade, ou entropia, para identificadores de sessão deve vir de um gerador de números aleatórios projetado para criptografia. No entanto, a inicialização desses geradores às vezes pode ser cara ou computacionalmente lenta; portanto, deve-se fazer um esforço para reutilizá-los quando possível.
Use como identificadores de objeto
Nem todo aplicativo requer segurança. A atribuição aleatória pode ser uma maneira eficiente de várias entidades gerarem identificadores em um espaço compartilhado sem nenhuma coordenação ou particionamento. A coordenação pode ser lenta, especialmente em um ambiente em cluster ou distribuído, e a divisão de um espaço causa problemas quando as entidades terminam com compartilhamentos muito pequenos ou grandes.
Os identificadores gerados sem tomar medidas para torná-los imprevisíveis devem ser protegidos por outros meios, se um invasor puder visualizá-los e manipulá-los, como acontece na maioria dos aplicativos da web. Deve haver um sistema de autorização separado que proteja objetos cujo identificador possa ser adivinhado por um invasor sem permissão de acesso.
Também é preciso ter cuidado para usar identificadores que sejam longos o suficiente para tornar improváveis as colisões, dado o número total previsto de identificadores. Isso é conhecido como "o paradoxo do aniversário". A probabilidade de uma colisão, p , é aproximadamente n 2 / (2q x ), onde n é o número de identificadores realmente gerados, q é o número de símbolos distintos no alfabeto ex é o comprimento dos identificadores. Esse deve ser um número muito pequeno, como 2 a 50 ou menos.
Trabalhar com isso mostra que a chance de colisão entre os 500k identificadores de 15 caracteres é de cerca de 2 a 52 , o que provavelmente é menos provável do que erros não detectados dos raios cósmicos, etc.
Comparação com UUIDs
De acordo com suas especificações, os UUIDs não foram projetados para serem imprevisíveis e não devem ser usados como identificadores de sessão.
Os UUIDs em seu formato padrão ocupam muito espaço: 36 caracteres para apenas 122 bits de entropia. (Nem todos os bits de um UUID "aleatório" são selecionados aleatoriamente.) Uma sequência alfanumérica escolhida aleatoriamente embala mais entropia em apenas 21 caracteres.
UUIDs não são flexíveis; eles têm uma estrutura e layout padronizados. Esta é sua principal virtude, bem como sua principal fraqueza. Ao colaborar com uma parte externa, a padronização oferecida pelos UUIDs pode ser útil. Para uso puramente interno, eles podem ser ineficientes.