Saltar al contenido

PHP: ¿Cómo generar una cadena alfanumérica única y aleatoria para usar en un enlace secreto?

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() y uniqid() 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!

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *