¿Cómo funciona realmente el generador de cadenas aleatorias en PHP? Echamos un vistazo bajo el capó
¿Alguna vez te has preguntado qué sucede cuando haces clic en «Generar contraseña» en un sitio web? ¿O cuando el sistema crea un identificador único para tu sesión? En nueve de cada diez casos, detrás de esto hay un generador de cadenas aleatorias en PHP. Parecería que no podría ser más sencillo: tomas un array de caracteres, lo mezclas y devuelves el resultado. Pero el diablo, como siempre, está en los detalles.
En Stack Overflow, la pregunta «PHP random string generator» ha acumulado miles de visitas. Y no es casualidad. Los novatos escriben soluciones caseras con rand(), los experimentados usan random_int(), y los gurús discuten sobre entropía. Analicemos cómo funciona realmente todo y escribamos nuestro generador ideal.
¿Por qué rand() es malo? (spoiler: predecibilidad)
La forma «infantil» más popular es usar rand() en un bucle:
<?phpfunction generateRandomString($length = 10) { $characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; $charactersLength = strlen($characters); $randomString = ''; for ($i = 0; $i < $length; $i++) { $randomString .= $characters[rand(0, $charactersLength - 1)]; } return $randomString;}echo generateRandomString(); // Salida: "aB3kL9xQ2m"?>El código funciona. Pero solo si no te importa la seguridad. La función rand() utiliza un generador lineal congruencial (LCG). Es un algoritmo determinista: conociendo el estado actual, se pueden predecir todos los números siguientes. Además, rand() se inicializa por defecto con uno, a menos que hayas llamado a srand(). Y si lo haces, la semilla a menudo se toma de time(), lo que da solo 2-3 millones de opciones. La fuerza bruta lleva segundos.
Conclusión: rand() sirve para juegos y selección aleatoria de avatares, pero no es adecuado en absoluto para generar tokens, contraseñas o claves de API.
Presentamos mt_rand() — más rápido, pero no más seguro
En PHP 4 apareció mt_rand(), basado en el Mersenne Twister. Es más rápido y tiene un mejor período (2^19937 frente a 2^31 de rand). Muchos todavía lo usan como una versión «avanzada»:
<?phpfunction generateTokenMtRand($length = 32) { $chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'; $token = ''; for ($i = 0; $i < $length; $i++) { $token .= $chars[mt_rand(0, strlen($chars) - 1)]; } return $token;}?>Pero aquí también hay un truco. El Mersenne Twister es criptográficamente no seguro. A partir de una secuencia de 624 números generados, se puede reconstruir el estado interno del generador y predecir todos los valores futuros. Para atacar un token de restablecimiento de contraseña, esto es más que suficiente.
Moraleja: mt_rand() es una excelente opción para simulaciones y pruebas, pero no para seguridad.
La santísima trinidad: random_int(), CSPRNG y entropía
A partir de PHP 7.0, tenemos random_int() y random_bytes(). Estos son generadores de números pseudoaleatorios criptográficamente seguros (CSPRNG). ¿Qué significa esto en la práctica?
- Entropía: PHP recopila «ruido» del sistema: tiempo entre pulsaciones de teclas, movimientos del ratón, paquetes de red, interrupciones del disco. En Linux se usa
/dev/urandom, en Windows —CryptGenRandom. - Impredecibilidad: Incluso conociendo el algoritmo, no se puede reconstruir la semilla ni predecir el siguiente número.
- Distribución uniforme: Cada byte aparece con una probabilidad exacta de 1/256.
Así es como se ve un generador de cadenas aleatorias correcto en PHP