¿Cómo generar una cadena alfanumérica única y aleatoria?

Resuelto Andrew asked hace 54 años • 32 respuestas

¿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?

Andrew avatar Jan 01 '70 08:01 Andrew
Aceptado

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 uniqidse basa en el tiempo y, según php.net, "el valor de retorno es un poco diferente del microtime()", uniqidno 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

Scott avatar Dec 05 '2012 22:12 Scott

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()y uniqid()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))
loletech avatar Dec 04 '2009 10:12 loletech

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!

Slava Fomin II avatar Jun 04 '2014 09:06 Slava Fomin II