¿Cómo generar una cadena alfanumérica única y aleatoria?
¿Cómo sería posible generar una cadena única y aleatoria usando números y letras para usar en un enlace de verificación? Como cuando creas una cuenta en un sitio web y te envía un correo electrónico con un enlace y tienes que hacer clic en ese enlace para verificar tu cuenta.
¿Cómo puedo generar uno de esos usando PHP?
La biblioteca estándar PHP 7 proporciona la random_bytes($length)
función que genera bytes pseudoaleatorios criptográficamente seguros.
Ejemplo:
$bytes = random_bytes(20);
var_dump(bin2hex($bytes));
El ejemplo anterior generará algo similar a:
string(40) "5fe69c95ed70a9869d9f9af7d8400a6673bb9ce9"
Más información: http://php.net/manual/en/function.random-bytes.php
PHP 5 (obsoleto)
Estaba investigando cómo resolver el mismo problema, pero también quiero que mi función cree un token que también pueda usarse para recuperar contraseñas. Esto significa que necesito limitar la capacidad de adivinar el token. Porque uniqid
se basa en el tiempo y, según php.net, "el valor de retorno es un poco diferente del microtime()", uniqid
no cumple con los criterios. PHP recomienda usarlo openssl_random_pseudo_bytes()
en su lugar para generar tokens criptográficamente seguros.
Una respuesta rápida, breve y directa es:
bin2hex(openssl_random_pseudo_bytes($bytes))
lo que generará una cadena aleatoria de caracteres alfanuméricos de longitud = $bytes * 2. Desafortunadamente, esto solo tiene un alfabeto de [a-f][0-9]
, pero funciona.
A continuación se muestra la función más sólida que pude crear y que satisface los criterios (esta es una versión implementada de la respuesta de Erik).
function crypto_rand_secure($min, $max)
{
$range = $max - $min;
if ($range < 1) return $min; // not so random...
$log = ceil(log($range, 2));
$bytes = (int) ($log / 8) + 1; // length in bytes
$bits = (int) $log + 1; // length in bits
$filter = (int) (1 << $bits) - 1; // set all lower bits to 1
do {
$rnd = hexdec(bin2hex(openssl_random_pseudo_bytes($bytes)));
$rnd = $rnd & $filter; // discard irrelevant bits
} while ($rnd > $range);
return $min + $rnd;
}
function getToken($length)
{
$token = "";
$codeAlphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
$codeAlphabet.= "abcdefghijklmnopqrstuvwxyz";
$codeAlphabet.= "0123456789";
$max = strlen($codeAlphabet); // edited
for ($i=0; $i < $length; $i++) {
$token .= $codeAlphabet[crypto_rand_secure(0, $max-1)];
}
return $token;
}
crypto_rand_secure($min, $max)
funciona como un reemplazo directo para rand()
o mt_rand
. Utiliza openssl_random_pseudo_bytes para ayudar a crear un número aleatorio entre $min y $max.
getToken($length)
crea un alfabeto para usar dentro del token y luego crea una cadena de longitud $length
.
Fuente: https://www.php.net/manual/en/function.openssl-random-pseudo-bytes.php#104322
Aviso de seguridad : esta solución no debe utilizarse en situaciones en las que la calidad de su aleatoriedad pueda afectar la seguridad de una aplicación. En particular,
rand()
yuniqid()
no son generadores de números aleatorios criptográficamente seguros . Consulte la respuesta de Scott para conocer una alternativa segura.
Si no necesita que sea absolutamente único en el tiempo:
md5(uniqid(rand(), true))
De lo contrario (dado que ya ha determinado un inicio de sesión único para su usuario):
md5(uniqid($your_user_login, true))
Versión orientada a objetos de la solución más votada
Creé una solución orientada a objetos basada en la respuesta de Scott :
<?php
namespace Utils;
/**
* Class RandomStringGenerator
* @package Utils
*
* Solution taken from here:
* http://stackoverflow.com/a/13733588/1056679
*/
class RandomStringGenerator
{
/** @var string */
protected $alphabet;
/** @var int */
protected $alphabetLength;
/**
* @param string $alphabet
*/
public function __construct($alphabet = '')
{
if ('' !== $alphabet) {
$this->setAlphabet($alphabet);
} else {
$this->setAlphabet(
implode(range('a', 'z'))
. implode(range('A', 'Z'))
. implode(range(0, 9))
);
}
}
/**
* @param string $alphabet
*/
public function setAlphabet($alphabet)
{
$this->alphabet = $alphabet;
$this->alphabetLength = strlen($alphabet);
}
/**
* @param int $length
* @return string
*/
public function generate($length)
{
$token = '';
for ($i = 0; $i < $length; $i++) {
$randomKey = $this->getRandomInteger(0, $this->alphabetLength);
$token .= $this->alphabet[$randomKey];
}
return $token;
}
/**
* @param int $min
* @param int $max
* @return int
*/
protected function getRandomInteger($min, $max)
{
$range = ($max - $min);
if ($range < 0) {
// Not so random...
return $min;
}
$log = log($range, 2);
// Length in bytes.
$bytes = (int) ($log / 8) + 1;
// Length in bits.
$bits = (int) $log + 1;
// Set all lower bits to 1.
$filter = (int) (1 << $bits) - 1;
do {
$rnd = hexdec(bin2hex(openssl_random_pseudo_bytes($bytes)));
// Discard irrelevant bits.
$rnd = $rnd & $filter;
} while ($rnd >= $range);
return ($min + $rnd);
}
}
Uso
<?php
use Utils\RandomStringGenerator;
// Create new instance of generator class.
$generator = new RandomStringGenerator;
// Set token length.
$tokenLength = 32;
// Call method to generate random string.
$token = $generator->generate($tokenLength);
Alfabeto personalizado
Puede utilizar un alfabeto personalizado si es necesario. Simplemente pase una cadena con caracteres admitidos al constructor o configurador:
<?php
$customAlphabet = '0123456789ABCDEF';
// Set initial alphabet.
$generator = new RandomStringGenerator($customAlphabet);
// Change alphabet whenever needed.
$generator->setAlphabet($customAlphabet);
Aquí están las muestras de salida.
SRniGU2sRQb2K1ylXKnWwZr4HrtdRgrM
q1sRUjNq1K9rG905aneFzyD5IcqD4dlC
I0euIWffrURLKCCJZ5PQFcNUCto6cQfD
AKwPJMEM5ytgJyJyGqoD5FQwxv82YvMr
duoRF6gAawNOEQRICnOUNYmStWmOpEgS
sdHUkEn4565AJoTtkc8EqJ6cC4MLEHUx
eVywMdYXczuZmHaJ50nIVQjOidEVkVna
baJGt7cdLDbIxMctLsEBWgAw5BByP5V0
iqT0B2obq3oerbeXkDVLjZrrLheW4d8f
OUQYCny6tj2TYDlTuu1KsnUyaLkeObwa
Espero que ayude a alguien. ¡Salud!