He estado buscando un algoritmo Java simple para generar una cadena alfanumérica pseudoaleatoria. En mi situación, se usaría como un identificador de sesión / clave único que "probablemente" sería único durante la 500K+
generación (mis necesidades realmente no requieren nada mucho más sofisticado).
Idealmente, podría especificar una longitud dependiendo de mis necesidades únicas. Por ejemplo, una cadena generada de longitud 12 podría parecerse a algo así "AEYGF7K0DM1X"
.
java
string
random
alphanumeric
Todd
fuente
fuente
Long.toHexString(Double.doubleToLongBits(Math.random()));
UUID.randomUUID().toString();
RandomStringUtils.randomAlphanumeric(12);
Respuestas:
Algoritmo
Para generar una cadena aleatoria, concatene caracteres dibujados aleatoriamente del conjunto de símbolos aceptables hasta que la cadena alcance la longitud deseada.
Implementación
Aquí hay un código bastante simple y muy flexible para generar identificadores aleatorios. Lea la información que sigue para obtener notas importantes sobre la aplicación.
Ejemplos de uso
Cree un generador inseguro para identificadores de 8 caracteres:
Cree un generador seguro para identificadores de sesión:
Cree un generador con códigos fáciles de leer para imprimir. Las cadenas son más largas que las cadenas alfanuméricas completas para compensar el uso de menos símbolos:
Usar como identificadores de sesión
Generar identificadores de sesión que probablemente sean únicos no es lo suficientemente bueno, o simplemente podría usar un contador simple. Los atacantes secuestran sesiones cuando se usan identificadores predecibles.
Hay tensión entre longitud y seguridad. Los identificadores más cortos son más fáciles de adivinar, porque hay menos posibilidades. Pero los identificadores más largos consumen más almacenamiento y ancho de banda. Un conjunto más grande de símbolos ayuda, pero puede causar problemas de codificación si los identificadores se incluyen en las URL o se vuelven a ingresar a mano.
La fuente subyacente de aleatoriedad, o entropía, para los identificadores de sesión debe provenir de un generador de números aleatorios diseñado para la criptografía. Sin embargo, la inicialización de estos generadores a veces puede ser computacionalmente costosa o lenta, por lo que se debe hacer un esfuerzo para reutilizarlos cuando sea posible.
Usar como identificadores de objeto
No todas las aplicaciones requieren seguridad. La asignación aleatoria puede ser una forma eficiente para que múltiples entidades generen identificadores en un espacio compartido sin ninguna coordinación o partición. La coordinación puede ser lenta, especialmente en un entorno agrupado o distribuido, y dividir un espacio causa problemas cuando las entidades terminan con recursos compartidos que son demasiado pequeños o demasiado grandes.
Los identificadores generados sin tomar medidas para hacerlos impredecibles deben protegerse por otros medios si un atacante puede verlos y manipularlos, como sucede en la mayoría de las aplicaciones web. Debe haber un sistema de autorización separado que proteja los objetos cuyo identificador pueda ser adivinado por un atacante sin permiso de acceso.
También se debe tener cuidado al usar identificadores que sean lo suficientemente largos como para hacer improbables las colisiones dado el número total anticipado de identificadores. Esto se conoce como "la paradoja del cumpleaños". La probabilidad de una colisión, p , es aproximadamente n 2 / (2q x ), donde n es el número de identificadores realmente generados, q es el número de símbolos distintos en el alfabeto yx es la longitud de los identificadores. Debe ser un número muy pequeño, como 2 a 50 o menos.
Resolver esto muestra que la posibilidad de colisión entre 500k identificadores de 15 caracteres es de 2 a 52 , lo que probablemente es menos probable que los errores no detectados de los rayos cósmicos, etc.
Comparación con UUID
Según su especificación, los UUID no están diseñados para ser impredecibles y no deben usarse como identificadores de sesión.
Los UUID en su formato estándar ocupan mucho espacio: 36 caracteres para solo 122 bits de entropía. (No todos los bits de un UUID "aleatorio" se seleccionan aleatoriamente). Una cadena alfanumérica elegida aleatoriamente contiene más entropía en solo 21 caracteres.
Los UUID no son flexibles; Tienen una estructura y diseño estandarizados. Esta es su principal virtud, así como su principal debilidad. Al colaborar con una parte externa, la estandarización ofrecida por UUID puede ser útil. Para uso puramente interno, pueden ser ineficientes.
fuente
.replaceAll("\\d", " ");
al final de lareturn new BigInteger(130, random).toString(32);
línea para hacer un intercambio de expresiones regulares. Reemplaza todos los dígitos con espacios. Funciona muy bien para mí: estoy usando esto como un sustituto de un front-end Lorem Ipsumsymbols
y utilizando un espacio; puede controlar la longitud promedio de "palabras" cambiando la cantidad de espacios en los símbolos (más ocurrencias para palabras más cortas). Para una solución de texto falso realmente exagerada, ¡puedes usar una cadena de Markov!SecureRandom
instancia asignada a larandom
variable.Java proporciona una forma de hacer esto directamente. Si no quieres los guiones, son fáciles de quitar. Solo usa
uuid.replace("-", "")
Salida:
fuente
UUID.randomUUID().toString().replaceAll("-", "");
hace que la cadena sea alfanumérica, según lo solicitado.fuente
SecureRandom
lugar de laRandom
clase. Si se generan contraseñas en un servidor, puede ser vulnerable a ataques de tiempo.AB = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
y algunos otros caracteres permitidos.static Random rnd = new Random();
dentro del método?Random
objeto en cada invocación de método? No lo creo.Si está contento de usar las clases de Apache, puede usar
org.apache.commons.text.RandomStringGenerator
(commons-text).Ejemplo:
Desde commons-lang 3.6,
RandomStringUtils
está en desuso.fuente
Apache Commons Lang 3.3.1
biblioteca mencionada , y solo se usajava.util.Random
para proporcionar secuencias aleatorias, por lo que produce secuencias inseguras .public static java.lang.String random(int count, int start, int end, boolean letters, boolean numbers, @Nullable char[] chars, java.util.Random random)
Puede usar la biblioteca Apache para esto: RandomStringUtils
fuente
compile 'commons-lang:commons-lang:2.6'
SecureRandom
y eres bueno.En una linea:
fuente
AEYGF7K0DM1X
que no es hexadecimal. Me preocupa con qué frecuencia la gente confunde alfanumérica con hexadecimal. No són la misma cosa.Math.random()
produce un valordouble
entre 0 y 1, por lo que la parte del exponente no se utiliza en su mayoría. Úselorandom.nextLong
para un azar enlong
lugar de este truco feo.Esto se puede lograr fácilmente sin bibliotecas externas.
1. Generación de datos pseudoaleatorios criptográficos
Primero necesitas un PRNG criptográfico. Java tiene
SecureRandom
para eso y generalmente usa la mejor fuente de entropía en la máquina (por ejemplo/dev/random
). Leer más aquí.Nota:
SecureRandom
es la forma más lenta pero más segura en Java de generar bytes aleatorios. Sin embargo, recomiendo NO considerar el rendimiento aquí, ya que generalmente no tiene un impacto real en su aplicación a menos que tenga que generar millones de tokens por segundo.2. Espacio requerido de valores posibles
A continuación, debe decidir "qué tan único" debe ser su token. El único punto de considerar la entropía es asegurarse de que el sistema pueda resistir los ataques de fuerza bruta: el espacio de valores posibles debe ser tan grande que cualquier atacante solo pueda probar una proporción insignificante de los valores en un tiempo no ridículo 1 . Los identificadores únicos como aleatorio
UUID
tienen 122 bits de entropía (es decir, 2 ^ 122 = 5.3x10 ^ 36): la posibilidad de colisión es "* (...) para que haya una posibilidad de duplicación de uno en mil millones, versión de 103 trillones Se deben generar 4 UUID de 2 ". Elegiremos 128 bits, ya que se ajusta exactamente a 16 bytes y se considera muy suficiente.por ser único para básicamente todos, pero los más extremos, use casos y no tenga que pensar en duplicados. Aquí hay una tabla de comparación simple de entropía que incluye un análisis simple del problema de cumpleaños .Para requisitos simples, 8 o 12 bytes de longitud pueden ser suficientes, pero con 16 bytes está en el "lado seguro".
Y eso es básicamente todo. Lo último es pensar en la codificación para que pueda representarse como un texto imprimible (leer, a
String
).3. Codificación de binario a texto
Las codificaciones típicas incluyen:
Base64
cada personaje codifica 6 bits creando una sobrecarga del 33%. Afortunadamente, hay implementaciones estándar en Java 8+ y Android . Con Java más antiguo, puede usar cualquiera de las numerosas bibliotecas de terceros . Si desea que sus tokens sean seguros para url, use la versión segura para url de RFC4648 (que generalmente es compatible con la mayoría de las implementaciones). Ejemplo de codificación de 16 bytes con relleno:XfJhfv3C0P6ag7y9VQxSbw==
Base32
cada personaje codifica 5 bits creando una sobrecarga del 40%. Esto lo usaráA-Z
y2-7
lo hará razonablemente eficiente en cuanto al espacio, al tiempo que no distingue entre mayúsculas y minúsculas. No hay implementación estándar en el JDK . Ejemplo de codificación de 16 bytes sin relleno:WUPIL5DQTZGMF4D3NX5L7LNFOY
Base16
(hexadecimal) cada carácter codifica 4 bits que requieren 2 caracteres por byte (es decir, 16 bytes crean una cadena de longitud 32). Por lo tanto, hex es menos eficiente que el espacioBase32
pero es seguro de usar en la mayoría de los casos (url) ya que solo usa0-9
yA
paraF
. Ejemplo codifica 16 bytes:4fa3dd0f57cb3bf331441ed285b27735
. Vea una discusión SO sobre la conversión a hexadecimal aquí.Existen codificaciones adicionales como Base85 y la exótica Base122 con mejor / peor eficiencia de espacio. Puede crear su propia codificación (que básicamente hacen la mayoría de las respuestas en este hilo) pero le aconsejaría que no lo haga, si no tiene requisitos muy específicos. Ver más esquemas de codificación en el artículo de Wikipedia.
4. Resumen y ejemplo
SecureRandom
hex
obase32
si necesita que sea alfanumérico)No
Ejemplo: generador de tokens hexadecimales
Ejemplo: Generador de tokens Base64 (Url Safe)
Ejemplo: herramienta CLI de Java
Si desea una herramienta cli lista para usar, puede usar dados: https://github.com/patrickfav/dice
Ejemplo: Problema relacionado: proteja sus ID actuales
Si ya tiene una identificación que puede usar (por ejemplo, un sintético
long
en su entidad), pero no desea publicar el valor interno , puede usar esta biblioteca para cifrarlo y ofuscarlo: https://github.com/patrickfav / id-maskfuente
BigInteger
s negativos utilizando un parámetro constructor: enBigInteger(1, token)
lugar deBigInteger(token)
.import java.security.SecureRandom;
yimport java.math.BigInteger;
son necesarios para que el ejemplo funcione, ¡pero funciona muy bien!new SecureRandom()
usos/dev/urandom
usar Dollar debería ser simple como:
produce algo como eso:
fuente
Aquí está en Java:
Aquí hay una muestra de ejecución:
fuente
Random#nextInt
onextLong
. Cambie aSecureRandom
si es necesario.Sorprendentemente, nadie aquí lo ha sugerido pero:
Fácil.
El beneficio de esto es que los UUID son agradables y largos y se garantiza que sean casi imposibles de colisionar.
Wikipedia tiene una buena explicación:
http://en.wikipedia.org/wiki/Universally_unique_identifier#Random_UUID_probability_of_duplicates
Los primeros 4 bits son el tipo de versión y 2 para la variante, por lo que obtienes 122 bits aleatorios. Entonces, si lo desea , puede truncar desde el final para reducir el tamaño del UUID. No se recomienda, pero aún tiene mucha aleatoriedad, suficiente para que sus 500k registros sean fáciles.
fuente
Una solución corta y fácil, pero usa solo minúsculas y números:
El tamaño es de aproximadamente 12 dígitos a la base 36 y no se puede mejorar aún más de esa manera. Por supuesto, puede agregar varias instancias.
fuente
Long.toString(Math.abs(r.nextLong()), 36);
abs
se resuelve utilizando un operador bit a bit para borrar el bit más significativo. Esto funcionará para todos los valores.<< 1 >>> 1
.Una alternativa en Java 8 es:
fuente
El uso de UUID es inseguro, porque partes del UUID no son aleatorias en absoluto. El procedimiento de @erickson es muy ordenado, pero no crea cadenas de la misma longitud. El siguiente fragmento debería ser suficiente:
¿Por qué elegir
length*5
? Asumamos el caso simple de una cadena aleatoria de longitud 1, entonces un carácter aleatorio. Para obtener un carácter aleatorio que contenga todos los dígitos 0-9 y caracteres az, necesitaríamos un número aleatorio entre 0 y 35 para obtener uno de cada carácter.BigInteger
proporciona un constructor para generar un número aleatorio, distribuido uniformemente en el rango0 to (2^numBits - 1)
. Lamentablemente, 35 no es un número que pueda recibirse por 2 ^ numBits - 1. Por lo tanto, tenemos dos opciones: ir con2^5-1=31
o2^6-1=63
. Si escogiéramos2^6
, obtendríamos muchos números "innecesarios" / "más largos". Por2^5
lo tanto, es la mejor opción, incluso si perdemos 4 caracteres (wz). Para generar ahora una cadena de cierta longitud, simplemente podemos usar un2^(length*numBits)-1
número. El último problema, si queremos una cadena con una cierta longitud, aleatorio podría generar un número pequeño, por lo que la longitud no se cumple, por lo que tenemos que rellenar la cadena a los ceros que preceden la longitud requerida.fuente
fuente
Entonces, lo que esto hace es agregar la contraseña a la cadena y ... sí, funciona bien, compruébalo ... muy simple. yo lo escribi
fuente
+ 0
eso a menudo? ¿Por qué se divide la declaración de spot y la inicialización? ¿Cuál es la ventaja de los índices 1,2,3,4 en lugar de 0,1,2,3? Lo más importante: tomó un valor aleatorio y lo comparó con if-else 4 veces un nuevo valor, que siempre podría no coincidir, sin obtener más aleatoriedad. Pero siéntase libre de retroceder.Encontré esta solución que genera una cadena codificada hexadecimal aleatoria. La prueba unitaria proporcionada parece cumplir con mi caso de uso principal. Aunque, es un poco más complejo que algunas de las otras respuestas proporcionadas.
fuente
Cambie los caracteres de cadena según sus requisitos.
La cadena es inmutable. Aquí
StringBuilder.append
es más eficiente que la concatenación de cadenas.fuente
Random
instancia en cada iteración del bucle es ineficiente.fuente
fuente
Realmente no me gusta ninguna de estas respuestas con respecto a la solución "simple": S
Iría por un simple;), Java puro, un trazador de líneas (la entropía se basa en la longitud de la cadena aleatoria y el conjunto de caracteres dado):
o (un poco más legible a la antigua)
Pero, por otro lado, también podría usar UUID, que tiene una entropía bastante buena ( https://en.wikipedia.org/wiki/Universally_unique_identifier#Collisions ):
Espero que ayude.
fuente
Menciona "simple", pero en caso de que alguien más esté buscando algo que cumpla con los requisitos de seguridad más estrictos, es posible que desee echar un vistazo a jpwgen . jpwgen está modelado a partir de pwgen en Unix, y es muy configurable.
fuente
Puede usar la clase UUID con su mensaje getLeastSignificantBits () para obtener 64 bits de datos aleatorios, luego convertirlo a un número de radix 36 (es decir, una cadena que consiste en 0-9, AZ):
Esto produce una cadena de hasta 13 caracteres de longitud. Usamos Math.abs () para asegurarnos de que no haya un signo menos a escondidas.
fuente
random.nextLong()
? O inclusoDouble.doubleToLongBits(Math.random())
?Puede usar el siguiente código, si su contraseña obligatoria contiene números caracteres alfabéticos especiales:
fuente
Aquí está el código de una línea de AbacusUtil
Aleatorio no significa que deba ser único. para obtener cadenas únicas, usando:
fuente
Aquí hay una solución Scala:
fuente
usando la biblioteca apache se puede hacer en una línea
aquí está el documento http://commons.apache.org/lang/api-2.3/org/apache/commons/lang/RandomStringUtils.html
fuente
fuente
Creo que esta es la solución más pequeña aquí, o casi una de las más pequeñas:
El código funciona bien. Si está utilizando este método, le recomiendo que use más de 10 caracteres. La colisión ocurre a 5 caracteres / 30362 iteraciones. Esto tomó 9 segundos.
fuente
fuente
length
lugar dechars.length
en el ciclo for:for (int i = 0; i < length; i++)
fuente