¿Qué tan único es uniqid?

76

Esta pregunta no es realmente un problema para buscar una solución, es más una simple cuestión de curiosidad. La función uniqid de PHP tiene un indicador de más entropía, para hacer que la salida sea "más única". Esto me hizo preguntarme, ¿qué tan probable es que esta función produzca el mismo resultado más de una vez cuando more_entropy es verdadero, versus cuando no lo es? En otras palabras, ¿qué tan único es uniqid cuando more_entropy está habilitado, en comparación con cuando está deshabilitado? ¿Hay algún inconveniente en tener more_entropy habilitado todo el tiempo?

GordonM
fuente
3
Si desea algo que sea siempre único, deberá implementar un GUID . Casi cualquier otra cosa eventualmente colisionará, ya que hay una cantidad limitada de entropía en la función. Por ejemplo, uniqidcon more_entropyset da solo unos 92 bits de entropía (23 hexbits). Para entender por qué eso no es bueno para la singularidad, consulte El problema del cumpleaños ...
ircmaxell
@ircmaxell gracias por señalar El problema del cumpleaños, es bastante interesante. Definitivamente debería mencionarse en la respuesta.
Petr Peller
2
uniqid () no es una función hash, por lo que The Birthday Problem no se aplica a ella. Sin embargo, tiene sus vulnerabilidades.
Joel Mellon
@ircmaxell ¿de dónde viene ese número? more_entropytiene aproximadamente 30 bits de entropía (nueve dígitos decimales), la parte de microsegundos es aproximadamente 20 (seis dígitos decimales), ¿de dónde viene el resto? Debería elegir el segundo de un rango de 100.000 años para obtener 42 bits de entropía.
Tgr

Respuestas:

36

Actualización, marzo de 2014:

En primer lugar, es importante tener en cuenta que uniqides un nombre poco apropiado, ya que no garantiza una identificación única.

Según la documentación de PHP :

¡ADVERTENCIA!

Esta función no crea una cadena aleatoria ni impredecible. Esta función no debe utilizarse con fines de seguridad. Utilice un generador / función aleatoria criptográficamente segura y funciones hash criptográficamente seguras para crear una identificación segura impredecible.

Y

Esta función no genera tokens criptográficamente seguros, de hecho, sin pasar ningún parámetro adicional, el valor de retorno es un poco diferente de microtime () . Si necesita generar tokens criptográficamente seguros, use openssl_random_pseudo_bytes () .


Establecer more-entropy en true genera un valor más único, sin embargo, el tiempo de ejecución es más largo (aunque en un grado mínimo), según los documentos:

Si se establece en TRUE, uniqid () agregará entropía adicional (usando el generador congruencial lineal combinado) al final del valor de retorno, lo que aumenta la probabilidad de que el resultado sea único.

Tenga en cuenta la línea increases the likelihood that the result will be uniquey no eso garantizará la singularidad.

Puede esforzarse 'sin cesar' por la singularidad, hasta cierto punto, y mejorar utilizando cualquier número de rutinas de cifrado, agregando sales y cosas por el estilo, depende del propósito.

Recomiendo mirar los comentarios sobre el tema principal de PHP, en particular:

http://www.php.net/manual/en/function.uniqid.php#96898

http://www.php.net/manual/en/function.uniqid.php#96549

http://www.php.net/manual/en/function.uniqid.php#95001

Lo que recomendaría es averiguar por qué necesita la singularidad, ¿es por seguridad (es decir, para agregar a una rutina de cifrado / codificación)? Además, ¿qué tan único debe ser? Finalmente, mire la consideración de velocidad. La idoneidad cambiará con las consideraciones subyacentes.

SW4
fuente
1
La lección más importante con esos comentarios de función es que uuid en sí mismo es un identificador muy peligroso para pasar como una cookie / ID legible por el cliente, pero como una ID única local / protegida tiene algunos buenos usos, a saber, la velocidad. 2,5 centavos.
DrPerdix
3
No sé si esto fue obvio todavía, pero no lo use uniqid(o sus derivados) para nada relacionado con la seguridad. PHP ofrece una gran cantidad de generadores aleatorios criptográficos de seguridad, tales como: openssl_random_pseudo_bytes. Utilice la herramienta adecuada para el trabajo.
Halcyon
1
Suponiendo que no se guarden 2 archivos en el mismo microsegundo, una marca de tiempo de microsegundos de Unix sería única para cada archivo.
CMCDragonkai
Es estadísticamente improbable que sufra una colisión, pero no imposible. Ponga su generación uniqid dentro de un do{} while(collision). Utilizo este enfoque al generar rutas para archivos cargados, por ejemplo.
afilina
2
No estoy seguro de por qué se aceptó esta respuesta. ¡Único! =
Aleatorio
16

Las cosas solo son únicas si comprueba que aún no existen. No importa qué función use para generar una cadena 'aleatoria', o ID; si no verifica que no sea un duplicado, siempre existe la posibilidad ...;)

Si bien uniqid se basa en la hora actual, la nota de advertencia anterior aún se aplica; solo depende de dónde usará estos "ID únicos". La clave de todo esto es donde dice "más singular". Único es único es único. ¡Cómo puedes tener algo que es más o menos único, me confunde un poco!

Verificar lo anterior y combinar todas estas cosas le permitirá terminar con algo que se acerca a la singularidad, pero todo está relacionado con dónde se usarán las claves y el contexto. ¡Espero que ayude!

dmp
fuente
10
Hay una gran diferencia entre "la probabilidad de sufrir una colisión es de uno en diez mil" y "el cambio de conseguir una colisión es menor que el que cada usuario del programa sea alcanzado por un rayo simultáneamente". Un valor de 128 bits generado por un buen RNG con una buena semilla está tan cerca de ser "realmente" único que no importa, considerando los costos increíblemente altos de obtener algo demostrablemente (e impredeciblemente) único.
Michael Borgwardt
6
Solo para avanzar en su punto @Michael: para 128 bits, necesitaría que todos en los EE. UU. (300 millones) generen 1 millón de números por segundo durante aproximadamente un día para tener un 50% de probabilidad de una colisión ... Para 512 bits, necesitaría que todos los cuerpos de la tierra (7 mil millones de personas) generen 1 billón de números por segundo cada uno durante los próximos 10^47años solo para tener un 50% de probabilidad de una colisión ... Así que sí, con un límite superior lo suficientemente alto en el número aleatorio Y un RNG lo suficientemente bueno, puede simular la singularidad con solo aleatoriedad ...
ircmaxell
1
Estoy completamente de acuerdo con sus ejemplos de mundo ideal como los anteriores. Las posibilidades son mínimas. Sin embargo, la aleatoriedad no es perfecta en las implementaciones a las que se hace referencia en la pregunta original, y mantengo que el dominio donde se usa este número único es importante. Si tuvieras 1000 servidores, cada uno haciendo ID 'únicos' basados ​​en microtaciones, y asumiendo que eran únicos "porque sí", entonces en algún momento, es posible que te quemes. Haciendo caso omiso de cualquier peculiaridad en el código ... errores, o lo que sea. La diferencia aquí es entre la realidad y la teoría, y es por eso que verificamos;)
dmp
5
"El principio de generar pequeñas cantidades de improbabilidad finita simplemente conectando los circuitos lógicos de un cerebro sub-mesón Bambleweeny 57 a un trazador de vectores atómicos suspendido en un fuerte productor de movimiento browniano (digamos una taza de té caliente) se entendió bien por supuesto . "
dmp
1
@ircmaxell: El problema es que esos números requieren una aleatoriedad real y, por lo tanto, un RNG real. Ni siquiera podría simularlo con un PRNG con> 128 bits de estado interno, a menos que también tuviera una forma de sembrarlo con un valor único / aleatorio> 128 bits. ¡Pero ese es el problema que tienes que resolver! Y cualquier cosa menos que eso, virtualmente garantiza colisiones. Esas mismas 300 millones de personas, si estuvieran usando el material de mierda de su compilador rand(), tendrían> 90% de posibilidades de colisión en la primera iteración . Además, si necesita singularidad, incluso una probabilidad de colisión del 0,001% es demasiado.
cHao
10

De las discusiones sobre la función en el sitio del manual de PHP:

Como señalan otros a continuación, sin prefijo y sin "entropía agregada", esta función simplemente devuelve la marca de tiempo UNIX con un contador de microsegundos agregado como un número hexadecimal; es más o menos solo microtime (), en forma hexit.

[...]

También vale la pena tener en cuenta que, dado que microtime () solo funciona en sistemas que tienen gettimeofday ()> presente, lo que Windows NO HACE de forma nativa, uniqid () puede producir solo la marca de tiempo UNIX de resolución de un solo segundo en un entorno Windows.

En otras palabras, sin "more_entropy", la función es absolutamente horrible y nunca debería usarse, punto. Según la documentación, la bandera usará un "generador congruencial lineal combinado" para "agregar entropía". Bueno, ese es un RNG bastante débil. Así que omitiría esta función por completo y usaría algo basado en mt_rand con una buena semilla para cosas que no son relevantes para la seguridad, y SHA-256 para cosas que sí lo son.

Michael Borgwardt
fuente
7

Sin el indicador more_unique, devuelve la marca de tiempo de Unix con un contador de microsegundos, por lo tanto, si se realizan dos llamadas en el mismo microsegundo, devolverán la misma identificación 'única'.

De ahí que sea una cuestión de qué tan probable es eso. La respuesta es no mucho, pero no en un grado descontable. Si necesita una identificación única y la genera con frecuencia (o trabaja con datos generados en otros lugares), no cuente con que sea absolutamente única.

Reese Moore
fuente
21
lo crea o no, en realidad nos llama sleep (1) para asegurarse de que eso nunca suceda.
Eli
2
@Eli no estoy seguro de si trollear o no, pero claramente ese no es el caso porque obtengo duplicados ejecutando esto: for ($ i = 0; $ i <10; $ i ++) echo uniqid (). "\norte";
djule5
3
@ djule5 No, no trolling: github.com/php/php-src/blob/… ¿Quizás está ejecutando una versión muy antigua de PHP o está en una plataforma para usleep que no existe?
Eli
@Eli interesante jaja gracias por la fuente! Estoy ejecutando PHP 5.5.11 pero estoy en Windows en esta máquina de desarrollo ... ¡así que eso probablemente lo explica! Entonces definitivamente no es tan único en Windows entonces ...
djule5
1
@ user5542121 decidieron no llamar a usleep y poll time en su lugar, ya que usleep "puede hacer que el kernel programe otro proceso, provocando una pausa de alrededor de 10ms" ~ github.com/php/php-src/blob/PHP-7.2.12/ ext / standard /…
x3ns
5

El bit relevante del código fuente es

if (more_entropy) {
    uniqid = strpprintf(0, "%s%08x%05x%.8F", prefix, sec, usec, php_combined_lcg() * 10);
} else {
    uniqid = strpprintf(0, "%s%08x%05x", prefix, sec, usec);
}

Por lo tanto, more_entropyagrega nueve dígitos decimales algo aleatorios ( php_combined_lcg()devuelve un valor en (0,1)); eso es 29.9 bits de entropía, como máximo (en realidad, probablemente menos, ya que LCG no es un generador de números pseudoaleatorios criptográficamente seguro).

Tgr
fuente