La capacidad de adivinar el siguiente valor rand
está vinculada a la capacidad de determinar con qué srand
se llamó. En particular, ¡ sembrar srand
con un número predeterminado da como resultado un resultado predecible ! Desde el mensaje interactivo de PHP:
[charles@charles-workstation ~]$ php -a
Interactive shell
php > srand(1024);
php > echo rand(1, 100);
97
php > echo rand(1, 100);
97
php > echo rand(1, 100);
39
php > echo rand(1, 100);
77
php > echo rand(1, 100);
93
php > srand(1024);
php > echo rand(1, 100);
97
php > echo rand(1, 100);
97
php > echo rand(1, 100);
39
php > echo rand(1, 100);
77
php > echo rand(1, 100);
93
php >
Esto no es solo una casualidad. La mayoría de las versiones de PHP * en la mayoría de las plataformas ** generarán la secuencia 97, 97, 39, 77, 93 cuando srand
esté en 1024.
Para ser claros, esto no es un problema con PHP, es un problema con la implementación de rand
sí mismo. El mismo problema aparece en otros idiomas que usan la misma implementación (o similar), incluido Perl.
El truco es que cualquier versión sensata de PHP se habrá sembrado previamente srand
con un valor "desconocido". Oh, pero no es realmente desconocido. De ext/standard/php_rand.h
:
#define GENERATE_SEED() (((long) (time(0) * getpid())) ^ ((long) (1000000.0 * php_combined_lcg(TSRMLS_C))))
Entonces, es algo de matemática con time()
, el PID y el resultado de php_combined_lcg
, que se define en ext/standard/lcg.c
. No voy a hacer c & p aquí, ya que, bueno, mis ojos estaban vidriosos y decidí dejar de cazar.
Un poco de Google muestra que otras áreas de PHP no tienen las mejores propiedades de generación de aleatoriedad , y llama a php_combined_lcg
destacar aquí, especialmente este bit de análisis:
Esta función ( gettimeofday
) no solo nos devuelve una marca de tiempo precisa del servidor en bandeja de plata, sino que también agrega salida LCG si solicitamos "más entropía" (de PHP uniqid
).
Si esouniqid
. Parece que el valor de php_combined_lcg
es lo que vemos cuando miramos los dígitos hexadecimales resultantes después de llamar uniqid
con el segundo argumento establecido en un valor verdadero.
Ahora, donde estabamos?
Oh si. srand
.
Por lo tanto, si el código del que está tratando de predecir valores aleatorios no llama srand
, tendrá que determinar el valor proporcionado por php_combined_lcg
, que puede obtener (¿indirectamente?) A través de una llamada a uniqid
. Con ese valor en la mano, es factible aplicar la fuerza bruta al resto del valor time()
, el PID y algunas matemáticas. El problema de seguridad vinculado se trata de romper sesiones, pero la misma técnica funcionaría aquí. De nuevo, del artículo:
Aquí hay un resumen de los pasos de ataque descritos anteriormente:
- Espere a que el servidor se reinicie
- buscar un valor uniqid
- fuerza bruta la semilla RNG de este
- sondear el estado en línea para esperar a que aparezca el objetivo
- intercalar encuestas de estado con encuestas uniqid para realizar un seguimiento de la hora actual del servidor y el valor RNG
- ID de sesión de fuerza bruta contra el servidor usando el intervalo de tiempo y valor de RNG establecido en el sondeo
Simplemente reemplace el último paso según sea necesario.
(Este problema de seguridad se informó en una versión anterior de PHP (5.3.2) de la que tenemos actualmente (5.3.6), por lo que es posible que el comportamiento de uniqid
y / o php_combined_lcg
haya cambiado, por lo que esto es específico técnica podría no ser viable por más tiempo. YMMV.)
Por otro lado, si el código que está tratando de llamarsrand
al producto se llama manualmente , a menos que estén usando algo muchas veces mejor que el resultado php_combined_lcg
, probablemente será mucho más fácil adivinar el valor y sembrar su local generador con el número correcto. La mayoría de las personas que llamarían manualmente srand
tampoco se darían cuenta de lo horrible que es esta idea y, por lo tanto, es probable que no usen mejores valores.
Vale la pena señalar que mt_rand
también se ve afectado por el mismo problema. La siembra mt_srand
con un valor conocido también producirá resultados predecibles. Basar su entropía openssl_random_pseudo_bytes
es probablemente una apuesta más segura.
tl; dr: Para obtener mejores resultados, no siembres el generador de números aleatorios de PHP y, por el amor de Dios, no expongas uniqid
a los usuarios. Hacer uno o ambos de estos puede hacer que sus números aleatorios sean más adivinables.
Actualización para PHP 7:
PHP 7.0 presenta random_bytes
y random_int
como funciones principales. Utilizan la implementación CSPRNG del sistema subyacente, lo que los libera de los problemas que tiene un generador de números aleatorios. Son efectivamente similares a openssl_random_pseudo_bytes
, solo sin necesidad de instalar una extensión. Un polyfill está disponible para PHP5 .
*: El parche de seguridad Suhosin cambia el comportamiento de rand
ymt_rand
siempre se reinicia con cada llamada. Suhosin es proporcionado por un tercero. Algunas distribuciones de Linux lo incluyen por defecto en sus paquetes oficiales de PHP, mientras que otros lo hacen una opción, y otros lo ignoran por completo.
**: Dependiendo de la plataforma y las llamadas a la biblioteca subyacente que se utilicen, se generarán diferentes secuencias de las documentadas aquí, pero los resultados aún deberían ser repetibles a menos que se use el parche Suhosin.