PHP: Как создать случайную уникальную буквенно-цифровую строку? - PullRequest
336 голосов
/ 04 декабря 2009

Как можно было бы генерировать случайную уникальную строку, используя цифры и буквы для использования в проверочной ссылке? Например, когда вы создаете учетную запись на веб-сайте, и она отправляет вам электронное письмо со ссылкой, и вам нужно щелкнуть эту ссылку, чтобы подтвердить свой аккаунт ... да ... один из них.

Как мне сгенерировать один из них, используя PHP?

Обновление: Только что вспомнил про uniqid(). Это функция PHP, которая генерирует уникальный идентификатор на основе текущего времени в микросекундах. Я думаю, что я буду использовать это.

Ответы [ 27 ]

453 голосов
/ 06 декабря 2012

Я просто смотрел, как решить эту проблему, но я также хочу, чтобы моя функция создавала токен, который также может быть использован для восстановления пароля. Это означает, что мне нужно ограничить способность токена угадываться. Поскольку uniqid основано на времени, и согласно php.net «возвращаемое значение мало отличается от microtime ()», uniqid не соответствует критериям. PHP рекомендует использовать openssl_random_pseudo_bytes() вместо генерации криптографически безопасных токенов.

Быстрый, короткий и точный ответ:

bin2hex(openssl_random_pseudo_bytes($bytes))

, который сгенерирует случайную строку буквенно-цифровых символов длиной = $ байтов * 2. К сожалению, у этого алфавита есть только [a-f][0-9], но он работает.


Ниже приведена самая сильная функция, которую я мог сделать, которая удовлетворяет критериям (это реализованная версия ответа Эрика).
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) работает как замена для rand() или mt_rand. Он использует openssl_random_pseudo_bytes, чтобы помочь создать случайное число между $ min и $ max.

getToken($length) создает алфавит для использования в токене, а затем создает строку длиной $length.

РЕДАКТИРОВАТЬ: Я не упомянул источник - http://us1.php.net/manual/en/function.openssl-random-pseudo-bytes.php#104322

EDIT (PHP7): С выпуском PHP7 стандартная библиотека теперь имеет две новые функции, которые могут заменить / улучшить / упростить функцию crypto_rand_secure, описанную выше. random_bytes($length) и random_int($min, $max)

http://php.net/manual/en/function.random-bytes.php

http://php.net/manual/en/function.random-int.php

* * Пример тысяча сорок-одиной: * +1042 *
function getToken($length){
     $token = "";
     $codeAlphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
     $codeAlphabet.= "abcdefghijklmnopqrstuvwxyz";
     $codeAlphabet.= "0123456789";
     $max = strlen($codeAlphabet); // edited

    for ($i=0; $i < $length; $i++) {
        $token .= $codeAlphabet[random_int(0, $max-1)];
    }

    return $token;
}
277 голосов
/ 04 декабря 2009

Уведомление о безопасности : Это решение не следует использовать в ситуациях, когда качество случайности может повлиять на безопасность приложения. В частности, rand() и uniqid() не являются криптографически безопасными генераторами случайных чисел . См. ответ Скотта для безопасной альтернативы.

Если вам не нужно, чтобы он был абсолютно уникальным со временем:

md5(uniqid(rand(), true))

В противном случае (если вы уже определили уникальный логин для своего пользователя):

md5(uniqid($your_user_login, true))
86 голосов
/ 04 июня 2014

Объектно-ориентированная версия решения с наибольшим количеством голосов

Я создал объектно-ориентированное решение на основе ответа Скотта :

<?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);
    }
}

Использование

<?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);

Пользовательский алфавит

При необходимости вы можете использовать пользовательский алфавит. Просто передайте строку с поддерживаемыми символами конструктору или установщику:

<?php

$customAlphabet = '0123456789ABCDEF';

// Set initial alphabet.
$generator = new RandomStringGenerator($customAlphabet);

// Change alphabet whenever needed.
$generator->setAlphabet($customAlphabet);

Вот выходные образцы

SRniGU2sRQb2K1ylXKnWwZr4HrtdRgrM
q1sRUjNq1K9rG905aneFzyD5IcqD4dlC
I0euIWffrURLKCCJZ5PQFcNUCto6cQfD
AKwPJMEM5ytgJyJyGqoD5FQwxv82YvMr
duoRF6gAawNOEQRICnOUNYmStWmOpEgS
sdHUkEn4565AJoTtkc8EqJ6cC4MLEHUx
eVywMdYXczuZmHaJ50nIVQjOidEVkVna
baJGt7cdLDbIxMctLsEBWgAw5BByP5V0
iqT0B2obq3oerbeXkDVLjZrrLheW4d8f
OUQYCny6tj2TYDlTuu1KsnUyaLkeObwa

Надеюсь, это кому-нибудь поможет. Ура!

32 голосов
/ 05 июня 2017

Я опоздал, но я здесь с некоторыми хорошими данными исследований, основанными на функциях, предоставленных ответом Скотта . Поэтому я настроил каплю Digital Ocean только для этого 5-дневного автоматического теста и сохранил сгенерированные уникальные строки в базе данных MySQL.

В течение этого тестового периода я использовал 5 разных длин (5, 10, 15, 20, 50) и +/- 0,5 миллиона записей было вставлено для каждой длины. Во время моего теста только длина 5 генерировала дубликаты +/- 3K из 0,5 миллиона, а оставшиеся длины не генерировали дубликатов. Таким образом, мы можем сказать, что если мы используем длину 15 или выше с функциями Скотта, то мы можем генерировать высоконадежные уникальные строки. Вот таблица с моими данными исследований:

enter image description here

Обновление: Я создал простое приложение Heroku, используя эти функции, которые возвращают токен в виде ответа JSON. Приложение доступно по адресу https://uniquestrings.herokuapp.com/api/token?length=15

Надеюсь, это поможет.

30 голосов
/ 24 сентября 2012

Эта функция генерирует случайный ключ, используя цифры и буквы:

function random_string($length) {
    $key = '';
    $keys = array_merge(range(0, 9), range('a', 'z'));

    for ($i = 0; $i < $length; $i++) {
        $key .= $keys[array_rand($keys)];
    }

    return $key;
}

echo random_string(50);

Пример вывода:

zsd16xzv3jsytnp87tk7ygv73k8zmr0ekh6ly7mxaeyeh46oe8
25 голосов
/ 11 августа 2016

Вы можете использовать UUID (универсальный уникальный идентификатор), его можно использовать для любых целей, от строки аутентификации пользователя до идентификатора платежной транзакции.

UUID - это 16-октетное (128-битное) число. В своей канонической форме UUID представлен 32 шестнадцатеричными цифрами, отображаемыми в пяти группах, разделенных дефисами, в форме 8-4-4-4-12, в общей сложности 36 символов (32 буквенно-цифровых символа и четыре дефиса).

function generate_uuid() {
    return sprintf( '%04x%04x-%04x-%04x-%04x-%04x%04x%04x',
        mt_rand( 0, 0xffff ), mt_rand( 0, 0xffff ),
        mt_rand( 0, 0xffff ),
        mt_rand( 0, 0x0C2f ) | 0x4000,
        mt_rand( 0, 0x3fff ) | 0x8000,
        mt_rand( 0, 0x2Aff ), mt_rand( 0, 0xffD3 ), mt_rand( 0, 0xff4B )
    );

}

// вызов функции

$transationID = generate_uuid();

некоторые примеры выходных данных будут выглядеть так:

E302D66D-87E3-4450-8CB6-17531895BF14
22D288BC-7289-442B-BEEA-286777D559F2
51B4DE29-3B71-4FD2-9E6C-071703E1FF31
3777C8C6-9FF5-4C78-AAA2-08A47F555E81
54B91C72-2CF4-4501-A6E9-02A60DCBAE4C
60F75C7C-1AE3-417B-82C8-14D456542CD7
8DE0168D-01D3-4502-9E59-10D665CEBCB2

надеюсь, это поможет кому-то в будущем :)

13 голосов
/ 12 октября 2014

Я использую этот однострочник:

base64_encode(openssl_random_pseudo_bytes(3 * ($length >> 2)));

где длина - длина желаемой строки (делится на 4, иначе она округляется до ближайшего числа, кратного 4)

7 голосов
/ 05 декабря 2016

Используйте код ниже, чтобы сгенерировать случайное число из 11 символов или изменить число согласно вашему требованию.

$randomNum=substr(str_shuffle("0123456789abcdefghijklmnopqrstvwxyz"), 0, 11);

или мы можем использовать пользовательскую функцию для генерации случайного числа

 function randomNumber($length){
     $numbers = range(0,9);
     shuffle($numbers);
     for($i = 0;$i < $length;$i++)
        $digits .= $numbers[$i];
     return $digits;
 }

 //generate random number
 $randomNum=randomNumber(11);
7 голосов
/ 04 декабря 2009
  1. Создать случайное число, используя ваше любимое случайное число генератор
  2. Умножить и разделить чтобы получить число, соответствующее номеру символов в вашем коде алфавит
  3. Получить предмет по этому индексу в ваш код алфавит.
  4. Повторите с 1), пока не получите длину, которую вы хочет

например (в псевдокоде)

int myInt = random(0, numcharacters)
char[] codealphabet = 'ABCDEF12345'
char random = codealphabet[i]
repeat until long enough
4 голосов
/ 18 июля 2013

Перед вами уникальный генератор идентификаторов. сделано мной.

<?php
$d=date ("d");
$m=date ("m");
$y=date ("Y");
$t=time();
$dmt=$d+$m+$y+$t;    
$ran= rand(0,10000000);
$dmtran= $dmt+$ran;
$un=  uniqid();
$dmtun = $dmt.$un;
$mdun = md5($dmtran.$un);
$sort=substr($mdun, 16); // if you want sort length code.

echo $mdun;
?>

вы можете повторить любое 'var' для вашего идентификатора, как вам нравится. но $ mdun лучше, вы можете заменить md5 на sha1 для лучшего кода, но это будет очень долго, что может вам не понадобиться.

Спасибо.

...