Una limpieza de tu código
Esto es lo mismo que estás haciendo, pero escrito un poco más elegante:
function random_str()
{
$secret = '';
while(strlen($secret) >= 20) {
$str = bin2hex(random_bytes(10));
$str = preg_replace('/[aeiouAEIOU]|\W+/', '', sha1(microtime() . mt_rand() . $str));
$secret .= $str;
}
return substr($secret, 0, 20);
}
Note que he eliminado la variable innecesaria $i
. (Lo sé, no es un problema de seguridad, pero de todos modos).
¿Es seguro?
El código es muy complicado, y toma un camino bastante extraño para resolver un problema no tan difícil. Algunos problemas:
- Usas el hash
sha1(microtime() . mt_rand() . $str)
cuando solo basándote en $str
sería suficiente. Ahí es donde se obtiene la entrada de un CSPRNG. Agregar algo de aleatoriedad no segura ( mt_rand
) y el tiempo y luego modificarla no la mejora.
- Como eliminas las vocales de una cadena hexadecimal, terminas usando solo el carácter
0123456789bcdf
. Eso es solo catorce, no mucho.
- Es un poco extraño generar primero una cadena que contenga los caracteres que no quieres y luego tirarlos, cuando solo podrías generar los que quieres.
Al final, el resultado es criptográficamente seguro, ya que se basa en random_bytes()
, eso es un CSPRNG. Pero las peculiaridades de su código y su falta de comprensión lo llevan a la inseguridad, por ejemplo. en este caso, podría terminar con menos entropía por personaje de lo que hubiera esperado.
Una mejor manera de hacerlo
Use la función PHP random_int
para elegir un personaje de un alfabeto fijo al azar :
function random_string($length, $alphabet) {
$result = ''; //Initialize an empty string.
$max = strlen($alphabet) - 1; //The maximum random number we want to generate.
for($i = 0; $i < $length; $i++) { //Do once for every character in the string.
$result .= $alphabet[random_int(0, $max)]; //Add a random letter from the alphabet.
}
return $result;
}
El rendimiento probablemente podría mejorarse eliminando la concatenación de cadenas en el bucle, pero probablemente no sea un gran problema.