PHP: Как создать случайную уникальную буквенно-цифровую строку?

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

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

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

35 ответов

Решение

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

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

md5(uniqid(rand(), true))

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

md5(uniqid($your_user_login, true))

Я просто смотрел, как решить эту проблему, но я также хочу, чтобы моя функция создавала токен, который также можно использовать для получения пароля. Это означает, что мне нужно ограничить способность токена угадываться. Так как 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

РЕДАКТИРОВАТЬ (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

Пример:

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

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

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

<?php

namespace Utils;

/**
 * Class RandomStringGenerator
 * @package Utils
 *
 * Solution taken from here:
 * http://stackru.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

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

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

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

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

Вы можете использовать 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

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

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

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

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

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

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

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

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

or we can use custom function to generate the random number

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

Для действительно случайных строк вы можете использовать

<?php

echo md5(microtime(true).mt_Rand());

выходы:

40a29479ec808ad4bcff288a48a25d5c

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

  1. Генерация случайного числа с помощью вашего любимого генератора случайных чисел
  2. Умножьте и разделите его, чтобы получить число, соответствующее количеству символов в вашем алфавите кода
  3. Получить элемент по этому индексу в вашем кодовом алфавите.
  4. Повторите с 1), пока у вас есть длина, которую вы хотите

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

int myInt = random(0, numcharacters)
char[] codealphabet = 'ABCDEF12345'
char random = codealphabet[i]
repeat until long enough

Это простая функция, которая позволяет генерировать случайные строки, содержащие буквы и цифры (буквенно-цифровые). Вы также можете ограничить длину строки. Эти случайные строки могут быть использованы для различных целей, в том числе: код реферала, промокод, код купона. Функция опирается на следующие функции PHP:base_convert, sha1, uniqid, mt_rand

function random_code($length)
{
  return substr(base_convert(sha1(uniqid(mt_rand())), 16, 36), 0, $length);
}

echo random_code(6);

/*sample output
* a7d9e8
* 3klo93
*/

При попытке создать случайный пароль вы пытаетесь:

  • Сначала создайте криптографически безопасный набор случайных байтов

  • Во-вторых, превращение этих случайных байтов в строку для печати

Теперь есть несколько способов генерировать случайные байты в php, например:

$length = 32;

//PHP 7+
$bytes= random_bytes($length);

//PHP < 7
$bytes= openssl_random_pseudo_bytes($length);

Затем вы хотите превратить эти случайные байты в печатную строку:

Вы можете использовать bin2hex:

$string = bin2hex($bytes);

или base64_encode:

$string = base64_encode($bytes);

Однако обратите внимание, что вы не контролируете длину строки, если используете base64. Вы можете использовать bin2hex, чтобы сделать это, использование 32 байтов превратится в строку из 64 символов. Но это будет работать только так в строке EVEN.

Вот что я использую:

md5(time() . rand());    
// Creates something like 0c947c3b1047334f5bb8a3b7adc1d97b

Простой однострочный хэш-генератор строки, например 77zd43-7bc495-64cg9a-535548 (1 байт = 2 символа)

        $hash = implode('-', [
       bin2hex(random_bytes(3)),
       bin2hex(random_bytes(3)),
       bin2hex(random_bytes(3)),
       bin2hex(random_bytes(3)),
    ]);

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

<?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 для лучшего кода, но это будет очень долго, что может вам и не понадобиться.

Спасибо.

Этот народ захлебывается стаканом воды ...

$random= substr(str_shuffle("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ*.-_"), 0, 10);

Простой. Возможность повторения этой случайной строки составляет 0,000000000000000000000000000001^70

Если вы хотите сгенерировать уникальную строку в PHP, попробуйте следующее.

md5(uniqid().mt_rand());

В этом,

uniqid() - Он будет генерировать уникальную строку. Эта функция возвращает уникальный идентификатор на основе метки времени в виде строки.

mt_rand() - Генерация случайного числа.

md5() - Он сгенерирует хеш-строку.

function random_string($length = 8) {
    $alphabets = range('A','Z');
    $numbers = range('0','9');
    $additional_characters = array('_','=');
    $final_array = array_merge($alphabets,$numbers,$additional_characters);
       while($length--) {
      $key = array_rand($final_array);

      $password .= $final_array[$key];
                        }
  if (preg_match('/[A-Za-z0-9]/', $password))
    {
     return $password;
    }else{
    return  random_string();
    }

 }

Мне нравится использовать хэш-ключи при работе с проверочными ссылками. Я бы порекомендовал использовать microtime и использовать хеширование с использованием MD5, поскольку не должно быть никаких причин, по которым ключи должны совпадать, поскольку хэши основаны на микротайме.

  1. $key = md5(rand());
  2. $key = md5(microtime());
<?php
function generateRandomString($length = 11) {
    $characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
    $charactersLength = strlen($characters);
    $randomString = '';
    for ($i = 0; $i < $length; $i++) {
        $randomString .= $characters[rand(0, $charactersLength - 1)];
    }
    return $randomString;

}

?>

Выше функция генерирует случайную строку длиной 11 символов.

      function codeGenerate() {
  $randCode  = (string)mt_rand(1000,9999);
  $randChar  = rand(65,90);
  $randInx   = rand(0,3);
  $randCode[$randInx] = chr($randChar);
  return $randCode;
}
echo codeGenerate();

ВЫХОД

      38I7
33V7
E836
736U

Я думаю это лучшее решение

Str::uuid()->toString()

Простое решение - преобразовать базу 64 в буквенно-цифровую, отбросив не буквенно-цифровые символы.

Здесь используется random_bytes() для криптографически безопасного результата.

function random_alphanumeric(int $length): string
{
    $result='';
    do
    {
        //Base 64 produces 4 characters for each 3 bytes, so most times this will give enough bytes in a single pass
        $bytes=random_bytes(($length+3-strlen($result))*2);
        //Discard non-alhpanumeric characters
        $result.=str_replace(['/','+','='],['','',''],base64_encode($bytes));
        //Keep adding characters until the string is long enough
        //Add a few extra because the last 2 or 3 characters of a base 64 string tend to be less diverse
    }while(strlen($result)<$length+3);
    return substr($result,0,$length);
}

После прочтения предыдущих примеров я придумал это:

protected static $nonce_length = 32;

public static function getNonce()
{
    $chars = array();
    for ($i = 0; $i < 10; $i++)
        $chars = array_merge($chars, range(0, 9), range('A', 'Z'));
    shuffle($chars);
    $start = mt_rand(0, count($chars) - self::$nonce_length);
    return substr(join('', $chars), $start, self::$nonce_length);
}

Я 10 раз продублирую массив [0-9,AZ] и перетасую элементы, после того как я получаю случайную начальную точку для substr(), чтобы быть более "креативной":), вы можете добавить [az] и другие элементы в массив, дублировать более или менее, будь более креативным, чем я

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

<?php
  function random_alphanumeric($length) {
    $chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ12345689';
    $my_string = '';
    for ($i = 0; $i < $length; $i++) {
      $pos = mt_rand(0, strlen($chars) -1);
      $my_string .= substr($chars, $pos, 1);
    }
    return $my_string;
  }
  echo random_alphanumeric(50); // 50 characters
?>

Он генерирует, например: Y1FypdjVbFCFK6Gh9FDJpe6dciwJEfV6MQGpJqAfuijaYSZ86g

Если вы хотите сравнить с другой строкой, чтобы убедиться, что это уникальная последовательность, вы можете использовать этот трюк...

$string_1 = random_alphanumeric(50);
$string_2 = random_alphanumeric(50);
while ($string_1 == $string_2) {
  $string_1 = random_alphanumeric(50);
  $string_2 = random_alphanumeric(50);
  if ($string_1 != $string_2) {
     break;
  }
}
echo $string_1;
echo "<br>\n";
echo $string_2;

он генерирует две уникальные строки:

qsBDs4JOoVRfFxyLAOGECYIsWvpcpMzAO9pypwxsqPKeAmYLOi

Ti3kE1WfGgTNxQVXtbNNbhhvvapnaUfGMVJecHkUjHbuCb85pF

Надеюсь, это то, что вы ищете...

можно использовать этот код. я тестировал 35 000 000 идентификаторов без дубликатов.

      <?php
$characters = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-_';
$result = '';
for ($i = 0; $i < 11; $i++)
    $result .= $characters[mt_rand(0, 63)];?>

вы можете изменить его по своему усмотрению. и если у вас есть какие-либо предложения, не стесняйтесь комментировать. Рекомендуется, чтобы вы проверили каждый идентификатор в своей базе данных, прежде чем использовать эти идентификаторы, сделав так, чтобы у вас были 100% уникальные идентификаторы в вашей базе данных.

Я добавил несколько специальных символов в ответ Скота, чтобы токен был более безопасным:

function getToken($length){
$token = "";
$codeAlphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
$codeAlphabet.= "abcdefghijklmnopqrstuvwxyz";
$codeAlphabet.= "0123456789";
$codeAlphabet.= "-_.~";  // Special characters allowed in url
$max = strlen($codeAlphabet);

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

return $token;
}

согласно https://developers.google.com/maps/documentation/urls/url-encoding:

эти специальные символы (-_.~) разрешены в URL-адресе, поэтому вы также можете использовать токен в качестве имени файла, но если вы просто хотите использовать токен в качестве значения параметра в URL-адресе, вы можете использовать больше специальных символов.

Я уверен что ты не можешь использовать & # + в значении параметра, но я тестировал следующие символы и может использоваться:

!@$%^*()-_.~

Вот забавный

      public function randomStr($length = 16) {
    $string = '';
        
    while (($len = strlen($string)) < $length) {
        $size = $length - $len;
            
        $bytes = random_bytes($size);
            
        $string .= substr(str_replace(['/', '+', '='], '', base64_encode($bytes)), 0, $size);
    }
        
        return $string;
}

Украдено из laravel здесь

Я думаю, что это лучший метод для использования.

str_shuffle(md5(rand(0,100000)))

Скотт, да ты очень пишешь и хорошее решение! Благодарю.

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

public function generateUniqueToken($userid, $username){

        $rand = mt_rand(100,999);
    $md5 = md5($userid.'!(&^ 532567_465 ///'.$username);

    $md53 = substr($md5,0,3);
    $md5_remaining = substr($md5,3);

    $md5 = $md53. $rand. $userid. $md5_remaining;

    return $md5;
}

Пожалуйста, посмотрите и дайте мне знать, если я смогу улучшить. Спасибо

Другие вопросы по тегам