Solución:
La biblioteca estándar de PHP 7 proporciona 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 (desactualizado)
Solo estaba buscando cómo resolver este mismo problema, pero también quiero que mi función cree un token que también se pueda usar para recuperar contraseñas. Esto significa que necesito limitar la capacidad de adivinar la ficha. Porque uniqid
se basa en la hora, y según php.net “el valor de retorno es un poco diferente de microtime ()”, uniqid
no cumple con los criterios. PHP recomienda usar openssl_random_pseudo_bytes()
en lugar de generar tokens criptográficamente seguros.
Una respuesta rápida, corta y al grano es:
bin2hex(openssl_random_pseudo_bytes($bytes))
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 hacer 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 de 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: http://us1.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. Vea la respuesta de Scott para una alternativa segura.
Si no necesita que sea absolutamente único a lo largo del 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
He creado una solución orientada a objetos basada en Scottla respuesta:
<?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 UtilsRandomStringGenerator;
// 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 compatibles al constructor o colocador:
<?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!