Eu estive procurando por um algoritmo Java simples para gerar uma seqüência alfanumérica pseudo-aleatória. Na minha situação, seria usado como um único identificador de sessão / chave que "provavelmente" seria único ao longo da 500K+
geração (minhas necessidades realmente não exigem nada muito mais sofisticado).
Idealmente, eu seria capaz de especificar um comprimento, dependendo das minhas necessidades de exclusividade. Por exemplo, uma sequência gerada de comprimento 12 pode se parecer com "AEYGF7K0DM1X"
.
java
string
random
alphanumeric
Todd
fonte
fonte
Long.toHexString(Double.doubleToLongBits(Math.random()));
UUID.randomUUID().toString();
RandomStringUtils.randomAlphanumeric(12);
Respostas:
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.
Exemplos de uso
Crie um gerador inseguro para identificadores de 8 caracteres:
Crie um gerador seguro para identificadores de sessão:
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:
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.
fonte
.replaceAll("\\d", " ");
até o final dareturn new BigInteger(130, random).toString(32);
linha para fazer uma troca de regex. Ele substitui todos os dígitos por espaços. Funciona muito bem para mim: eu estou usando isso como um substituto para um front-end Lorem Ipsumsymbols
e usando um espaço; você pode controlar o tamanho médio da "palavra" alterando o número de espaços nos símbolos (mais ocorrências para palavras mais curtas). Para uma solução de texto falso realmente exagerada, você pode usar uma cadeia de Markov!SecureRandom
instância atribuída àrandom
variável.Java fornece uma maneira de fazer isso diretamente. Se você não quer os traços, eles são fáceis de remover. Apenas use
uuid.replace("-", "")
Resultado:
fonte
UUID.randomUUID().toString().replaceAll("-", "");
torna a sequência alfanumérica, conforme solicitado.fonte
SecureRandom
vez daRandom
classe. Se as senhas forem geradas em um servidor, elas poderão ficar vulneráveis a ataques de tempo.AB = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
e alguns outros caracteres permitidos.static Random rnd = new Random();
dentro do método?Random
objeto em cada invocação de método? Acho que não.Se você está feliz em usar as classes do Apache, você pode usar
org.apache.commons.text.RandomStringGenerator
(texto comum).Exemplo:
Desde o commons-lang 3.6,
RandomStringUtils
está obsoleto.fonte
Apache Commons Lang 3.3.1
biblioteca - e está usando apenasjava.util.Random
para fornecer seqüências aleatórias, produzindo seqüências inseguras .public static java.lang.String random(int count, int start, int end, boolean letters, boolean numbers, @Nullable char[] chars, java.util.Random random)
Você pode usar a biblioteca Apache para isso: RandomStringUtils
fonte
compile 'commons-lang:commons-lang:2.6'
SecureRandom
e você é bom.Em uma linha:
fonte
AEYGF7K0DM1X
que não é hexadecimal. Preocupa-me quantas vezes as pessoas confundem alfanumérico com hexadecimal. Eles não são a mesma coisa.Math.random()
produzdouble
entre 0 e 1; portanto, a parte do expoente geralmente não é utilizada. Userandom.nextLong
para um aleatório emlong
vez deste truque feio.Isso é facilmente possível sem bibliotecas externas.
1. Geração de dados pseudo-aleatórios criptográficos
Primeiro, você precisa de um PRNG criptográfico. O Java tem
SecureRandom
isso e normalmente usa a melhor fonte de entropia na máquina (por exemplo/dev/random
). Leia mais aqui.Nota:
SecureRandom
é a maneira mais lenta, mas mais segura, em Java, de gerar bytes aleatórios. No entanto, recomendo NÃO considerar o desempenho aqui, pois geralmente não tem impacto real no seu aplicativo, a menos que você precise gerar milhões de tokens por segundo.2. Espaço Necessário de Valores Possíveis
Em seguida, você deve decidir "quão único" seu token precisa ser. O único e único ponto de considerar a entropia é garantir que o sistema resista a ataques de força bruta: o espaço de valores possíveis deve ser tão grande que qualquer invasor possa tentar apenas uma proporção desprezível dos valores em tempo não ridículo 1 . Identificadores exclusivos, como aleatórios, por serem exclusivos para basicamente todos os casos de uso, mas o mais extremo, e você não precisa pensar em duplicatas. Aqui está uma tabela de comparação simples de entropia, incluindo uma análise simples do
UUID
têm 122 bits de entropia (ou seja, 2 ^ 122 = 5,3x10 ^ 36) - a chance de colisão é "* (...) para haver uma chance em um bilhão de duplicação, versão de 103 trilhões de versões 4 UUIDs devem ser gerados 2 ". Escolheremos 128 bits, pois ele se encaixa exatamente em 16 bytes e é visto como altamente suficiente problema aniversário .Para requisitos simples, o comprimento de 8 ou 12 bytes pode ser suficiente, mas com 16 bytes você está no "lado seguro".
E é basicamente isso. A última coisa é pensar na codificação para que ela possa ser representada como um texto imprimível (leia, a
String
).3. Codificação binária para texto
Codificações típicas incluem:
Base64
cada caractere codifica 6 bits, criando uma sobrecarga de 33%. Felizmente, existem implementações padrão em Java 8+ e Android . Com o Java antigo, você pode usar qualquer uma das inúmeras bibliotecas de terceiros . Se você deseja que seus tokens sejam seguros, use a versão segura do URL do RFC4648 (que geralmente é suportada pela maioria das implementações). Exemplo de codificação de 16 bytes com preenchimento:XfJhfv3C0P6ag7y9VQxSbw==
Base32
cada caractere codifica 5 bits, criando uma sobrecarga de 40%. Isso o utilizaráA-Z
e o tornará2-7
razoavelmente eficiente em termos de espaço, além de ser alfanumérico que não diferencia maiúsculas de minúsculas. Não há implementação padrão no JDK . Exemplo de codificação de 16 bytes sem preenchimento:WUPIL5DQTZGMF4D3NX5L7LNFOY
Base16
(hex) todos os caracteres codificam 4 bits, exigindo 2 caracteres por byte (ou seja, 16 bytes criam uma cadeia de comprimento 32). Portanto, hex é menos eficiente em termos de espaço do queBase32
mas é seguro para uso na maioria dos casos (url), uma vez que apenas usa0-9
eA
paraF
. Exemplo 16 bytes que codifica:4fa3dd0f57cb3bf331441ed285b27735
. Veja uma discussão sobre SO sobre a conversão para hexadecimal aqui.Codificações adicionais como Base85 e a exótica Base122 existem com melhor / pior eficiência de espaço. Você pode criar sua própria codificação (o que basicamente a maioria das respostas deste segmento faz), mas eu desaconselharia se você não tiver requisitos muito específicos. Veja mais esquemas de codificação no artigo da Wikipedia.
4. Resumo e Exemplo
SecureRandom
hex
oubase32
se você precisar que seja alfanumérico)Não
Exemplo: Gerador de Token Hex
Exemplo: Gerador de Tokens Base64 (Url Seguro)
Exemplo: Ferramenta Java CLI
Se você deseja uma ferramenta CLI pronta para usar, pode usar os dados: https://github.com/patrickfav/dice
Exemplo: Problema relacionado - Proteja seus IDs atuais
Se você já possui um ID, pode usar (por exemplo, um sintético
long
na sua entidade), mas não deseja publicar o valor interno , pode usar esta biblioteca para criptografá-lo e ofuscar: https://github.com/patrickfav / id-maskfonte
BigInteger
s negativos usando um parâmetro construtor: emBigInteger(1, token)
vez deBigInteger(token)
.import java.security.SecureRandom;
eimport java.math.BigInteger;
são necessários para fazer o exemplo funcionar, mas funciona muito bem!new SecureRandom()
usos/dev/urandom
usar Dollar deve ser simples como:
ele gera algo assim:
fonte
Aqui está em Java:
Aqui está uma amostra de execução:
fonte
Random#nextInt
ounextLong
. Mude paraSecureRandom
se necessário.Surpreendente ninguém aqui sugeriu, mas:
Fácil.
O benefício disso é que os UUIDs são agradáveis e longos e garantidos que são quase impossíveis de colidir.
A Wikipedia tem uma boa explicação:
http://en.wikipedia.org/wiki/Universally_unique_identifier#Random_UUID_probability_of_duplicates
Os primeiros 4 bits são do tipo de versão e 2 para a variante, para que você obtenha 122 bits aleatoriamente. Portanto, se você quiser, pode truncar a partir do final para reduzir o tamanho do UUID. Não é recomendado, mas você ainda tem muita aleatoriedade, o suficiente para facilitar seus 500 mil registros.
fonte
Uma solução curta e fácil, mas usa apenas letras minúsculas e numéricas:
O tamanho tem cerca de 12 dígitos na base 36 e não pode ser melhorado ainda. Claro que você pode anexar várias instâncias.
fonte
Long.toString(Math.abs(r.nextLong()), 36);
abs
é resolvido usando um operador bit a bit para limpar o bit mais significativo. Isso funcionará para todos os valores.<< 1 >>> 1
.Uma alternativa no Java 8 é:
fonte
O uso de UUIDs é inseguro, porque partes do UUID não são aleatórias. O procedimento do @erickson é muito elegante, mas não cria sequências do mesmo comprimento. O seguinte trecho deve ser suficiente:
Por que escolher
length*5
. Vamos assumir o caso simples de uma sequência aleatória de comprimento 1, portanto, um caractere aleatório. Para obter um caractere aleatório contendo todos os dígitos de 0 a 9 e os caracteres az, precisaríamos de um número aleatório entre 0 e 35 para obter um de cada caractere.BigInteger
fornece um construtor para gerar um número aleatório, distribuído uniformemente pelo intervalo0 to (2^numBits - 1)
. Infelizmente 35 não é um número que pode ser recebido por 2 ^ numBits - 1. Portanto, temos duas opções: Ir com2^5-1=31
ou2^6-1=63
. Se escolhermos2^6
, obteremos muitos números "desnecessários" / "mais longos". Portanto,2^5
é a melhor opção, mesmo se perdermos 4 caracteres (wz). Para gerar agora uma sequência de um determinado comprimento, podemos simplesmente usar um2^(length*numBits)-1
número. O último problema, se quisermos uma string com um determinado comprimento, random poderá gerar um número pequeno, de modo que o comprimento não seja atingido, portanto, precisamos colocar a string no comprimento necessário de zeros.fonte
fonte
Então, o que isso faz é apenas adicionar a senha à string e ... sim, funciona bem, confira ... muito simples. Eu escrevi
fonte
+ 0
isso com frequência? Por que você divide a declaração de mancha e inicialização? Qual é a vantagem dos índices 1,2,3,4 em vez de 0,1,2,3? Mais importante: você pegou um valor aleatório e comparou com if-else 4 vezes um novo valor, que sempre pode ser incompatível, sem obter mais aleatoriedade. Mas fique à vontade para reverter.Eu encontrei esta solução que gera uma seqüência aleatória codificada em hexadecimal. O teste de unidade fornecido parece aguentar meu caso de uso principal. Embora seja um pouco mais complexo do que algumas das outras respostas fornecidas.
fonte
Altere os caracteres String de acordo com seus requisitos.
String é imutável. Aqui
StringBuilder.append
é mais eficiente que a concatenação de cadeias.fonte
Random
instância em cada iteração do loop é ineficiente.fonte
fonte
Não gosto realmente de nenhuma das respostas relacionadas à solução "simples": S
Eu iria para um simples;), java puro, um forro (a entropia é baseada no comprimento da seqüência aleatória e no conjunto de caracteres fornecido):
ou (um pouco mais legível)
Mas, por outro lado, você também pode usar o UUID, que possui uma entropia muito boa ( https://en.wikipedia.org/wiki/Universally_unique_identifier#Collisions ):
Espero que ajude.
fonte
Você menciona "simples", mas, para o caso de alguém procurar algo que atenda a requisitos de segurança mais rigorosos, dê uma olhada no jpwgen . O jpwgen é modelado após o pwgen no Unix e é muito configurável.
fonte
Você pode usar a classe UUID com sua mensagem getLeastSignificantBits () para obter 64 bits de dados aleatórios e convertê-los em um número radix 36 (ou seja, uma string que consiste em 0-9, AZ):
Isso gera uma String com até 13 caracteres. Usamos Math.abs () para garantir que não haja um sinal de menos entrando.
fonte
random.nextLong()
? Ou mesmoDouble.doubleToLongBits(Math.random())
?Você pode usar o código a seguir, se sua senha obrigatória contiver números caracteres alfabéticos especiais:
fonte
Aqui está o código de uma linha da AbacusUtil
Aleatório não significa que deve ser único. para obter strings exclusivas, usando:
fonte
Aqui está uma solução Scala:
fonte
usando a biblioteca apache, isso pode ser feito em uma linha
aqui está o documento http://commons.apache.org/lang/api-2.3/org/apache/commons/lang/RandomStringUtils.html
fonte
fonte
Eu acho que essa é a menor solução aqui, ou quase uma das menores:
O código funciona muito bem. Se você estiver usando esse método, recomendo que você use mais de 10 caracteres. A colisão ocorre em 5 caracteres / 30362 iterações. Isso levou 9 segundos.
fonte
fonte
length
lugar dochars.length
loop for: #for (int i = 0; i < length; i++)
fonte