PHP: Як створити випадкову, унікальну, буквено-цифрову рядок?


380

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

Як я можу створити один із тих, хто використовує PHP?

Оновлення: щойно згадав про uniqid(). Це функція PHP, яка генерує унікальний ідентифікатор на основі поточного часу в мікросекундах. Думаю, я цим скористаюся.


Все, що вам потрібно, це рядки та рівномірно розподілені випадкові числа.
Артелій

10
Гей Ендрю, ти повинен вибрати Scottправильну відповідь. Скотт використовує криптографічно захищений генератор псевдовипадкових чисел (CSPRNG) OpenSSL, який вибере найбільш безпечне джерело ентропії на основі вашої платформи.
грак

3
Усі, хто читає це після 2015 року, будь ласка, дивіться тут: paragonie.com/blog/2015/07/… Більшість найкращих відповідей мають більшу чи меншу кількість
помилок

OpenSSL і uniqidнебезпечні. Використовуйте щось на кшталт Random::alphanumericString($length)або Random::alphanumericHumanString($length).
каркати

Відповіді:


305

Повідомлення про безпеку : Це рішення не слід застосовувати в ситуаціях, коли якість вашої випадковості може вплинути на безпеку програми. Зокрема, rand()і uniqid()НЕ криптографически безпечний генератор випадкових чисел . Дивіться відповідь Скотта про безпечну альтернативу.

Якщо вам не потрібно, щоб він з часом був абсолютно унікальним:

md5(uniqid(rand(), true))

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

md5(uniqid($your_user_login, true))

90
Обидва способи не гарантують унікальність - довжина вводу функції md5 більша за довжину її виводу, і відповідно до en.wikipedia.org/wiki/Pigeonhole_principle зіткнення гарантується. З іншого боку, чим більша кількість населення, що покладається на хеші для досягнення "унікального" ідентифікатора, тим більша ймовірність виникнення хоча б одного зіткнення (див. En.wikipedia.org/wiki/Birthday_problem ). Ймовірність може бути невеликою для більшості рішень, але вона все ще існує.
Даріуш Вальчак

12
Це не безпечний метод генерації випадкових значень. Дивіться відповідь Скотта.
грак

4
Щодо підтверджень електронною поштою, які закінчуються відносно швидко, я вважаю, що крихітна можливість того, що одна людина може одного дня підтвердити чуже повідомлення електронної пошти, є незначним ризиком. Але ви завжди можете перевірити, чи маркер вже існує в БД після його створення, і просто вибрати новий, якщо це так. Однак час, який ви витрачаєте на написання фрагмента коду, швидше за все, не витрачається, оскільки він, швидше за все, ніколи не буде запущений.
Андрій

Зауважте, що md5 повертає шістнадцяткові значення, тобто набір символів обмежений [0-9] та [af].
Thijs Riezebeek

1
md5 може мати зіткнення, ви тільки що зруйнували перевагу
uniqid

507

Я просто розглядав, як вирішити цю саму проблему, але також хочу, щоб моя функція створила маркер, який можна використовувати і для пошуку паролів. Це означає, що мені потрібно обмежити здатність лексеми здогадуватися. Оскільки 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.

EDIT: Я знехтував цитувати джерело - 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

Приклад:

function getToken($length){
    $token = "";
    $codeAlphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
    $codeAlphabet.= "abcdefghijklmnopqrstuvwxyz";
    $codeAlphabet.= "0123456789";
    $max = strlen($codeAlphabet);

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

    return $token;
}

2
Домовились. Зберігання простого тексту паролем жахливо! (Я використовую blowfish.) Але після створення жетону він дозволяє власнику маркера скинути пароль, тому маркер є еквівалентним паролем, поки він дійсний (і трактується як такий).
Скотт

23
Гей, я поєднав ваш чудовий код із зручною функціональністю методу Kohana Text :: random () та створив цю суть: gist.github.com/raveren/5555297
raveren

9
Ідеально! Я випробував це з 10 символів як довжина. 100000 жетонів і ще немає дублікатів!
Sharpless512

5
Я використав цей процес і виявив його дуже повільним. З довжиною 36 він вийшов на сервер розробників. З тривалістю 24, це все ще зайняло близько 30 секунд. Не впевнений, що я зробив не так, але це було занадто повільно для мене, і я вибрав інше рішення.
Шейн

5
Ви також можете використовувати base64_encodeзамість, bin2hexщоб отримати коротший рядок з більшим алфавітом.
erjiang

91

Об'єктно-орієнтована версія найбільш підкресленого рішення

Я створив об'єктно-орієнтоване рішення на основі Скотта :

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

Сподіваюся, це комусь допоможе. Ура!


Вибачте, але як мені запустити цю річ? Я, $obj = new RandomStringGenerator;але який метод я повинен викликати? Дякую.
sg552

@ sg552, ви повинні викликати generateметод, як у прикладі вище. Просто передайте йому ціле число, щоб вказати довжину результуючого рядка.
Слава Фомін II

Дякую, це працює для мене, але Custom Alphabetне став. Я отримав порожній вихід, коли лунаю. У всякому разі, я навіть не впевнений, в чому користь другого прикладу, Custom Alphabetтому я думаю, що я просто залишаюся з першим прикладом. Дякую.
sg552

1
Приємно, але досить надмірно в більшості сценаріїв, якщо ваш проект сильно не покладається на випадково згенеровані коди
dspacejs

1
так приємно, але завищено, особливо якщо врахувати, що це в значній мірі застаріло для php 5.7
Ендрю

39

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

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

введіть тут опис зображення

Оновлення: я створив простий додаток Heroku за допомогою цих функцій, який повертає маркер як відповідь JSON. Доступ до нього можна отримати за адресою https://uniquestrings.herokuapp.com/api/token?length=15

Я сподіваюся, що це допомагає.


33

Ви можете використовувати 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

сподіваюся, що це допоможе комусь у майбутньому :)


Виглядає фантастично, додано до мого списку!
Мустафа Ердоган

32

Ця функція генерує випадковий ключ, використовуючи цифри та букви:

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

6
Через деякий час ви переходите до тих же цифр
Sharpless512,

1
Це не зовсім випадково, тому що він ніколи не буде мати один і той же характер більше, ніж один раз.
EricP

16

Я використовую цей однопластовий:

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

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


6
Він не завжди повертає лише буквено-цифрові символи. Він може містити символи типу==
user3459110

1
Якщо використовується URL-адреса, base64url_encodeїї можна використовувати замість цього, дивіться тут: php.net/manual/en/function.base64-encode.php
Павло

9

Використовуйте код нижче, щоб створити випадкову кількість 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);

3
Це ніколи не може генерувати рядок "aaaaaaaaaaa" або "aaaaabbbbbb". Він просто підбирає підрядку випадкової перестановки алфавіту. Джерело з 11 символів має лише V (11,35) = 288 можливих значень. Не використовуйте це, якщо ви очікуєте, що струни будуть унікальними!
хтоjeteri

@Peping тоді робітьsubstr(str_shuffle(str_repeat('0123456789abcdefghijklmnopqrstvwxyz', 36)), 0, 11);
Тім

7
  1. Створіть випадкове число за допомогою улюбленого генератора випадкових чисел
  2. Помножте та розділіть його, щоб отримати число, що відповідає кількості символів у вашому алфавіті коду
  3. Отримайте елемент у цьому індексі у своєму алфавіті коду.
  4. Повторюйте з 1), поки не буде потрібна довжина

наприклад (у псевдокоді)

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

Теоретично кажучи, чи не могло це потенційно генерувати один і той же рядок не один раз?
frezq

4

Ось унікальний унікальний генератор id для вас. зроблений мною.

<?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 для кращого коду, але це буде дуже довго, що, можливо, вам не знадобиться.

Дякую.


7
У цьому випадку випадковість гарантується випадковим письмовим кодом, правда?
Даніель Кукал

3
так, це свого роду "випадковий", оскільки це нестандартний, незрозумілий код. Це ускладнює передбачення ... але насправді він використовує незахищені частини для генерації випадкового числа. тому результат технічно не випадковий чи безпечний.
Філіп

4

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

<?php

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

Виходи:

40a29479ec808ad4bcff288a48a25d5c

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


4

Коли ви намагаєтеся генерувати випадковий пароль, ви намагаєтесь:

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

  • Друге - це перетворення цих випадкових байтів у рядок для друку

Тепер існує кілька способів генерування випадкових байтів у 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 символьну рядок . Але це працюватиме лише так, як ВИХІД рядку .

Отже, ви можете просто зробити :

$length = 32;

if(PHP_VERSION>=7){
    $bytes= random_bytes($length);
}else{
    $bytes= openssl_random_pseudo_bytes($length);
} 

$string = bin2hex($bytes);


2
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();
    }

 }

2

Це проста функція, яка дозволяє генерувати випадкові рядки, що містять букви та цифри (буквено-цифрові). Ви також можете обмежити довжину рядка. Ці випадкові рядки можна використовувати для різних цілей, включаючи: Код пересилання, Промо-код, Код купона. Функція покладається на такі функції 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
*/

1

прочитавши попередні приклади, я придумав це:

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] та інші елементи для масиву, дублювати більш-менш бути творчішим за мене


1

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

  1. $key = md5(rand());
  2. $key = md5(microtime());

1
<?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 символів.


1

Якщо ви хочете створити унікальну рядок у PHP, спробуйте виконати наступне.

md5(uniqid().mt_rand());

У цьому,

uniqid()- Це створить унікальний рядок. Ця функція повертає унікальний ідентифікатор на основі часової позначки у вигляді рядка.

mt_rand() - Утворити випадкове число.

md5() - Це створить хеш-рядок.


0

Скотт, так, ти дуже пишеш і гарне рішення! Дякую.

Мені також потрібно створити унікальний маркер API для кожного мого користувача. Слідом за моїм підходом я використовував інформацію про користувачів (Userid та Ім'я користувача):

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

Будь ласка, подивіться і дайте мені знати, чи я можу зробити якісь поліпшення. Дякую


0

Ось, що я використовую в одному зі своїх проектів, він чудово працює, і він генерує УНІКАЛЬНИЙ СКОРОЧНИЙ ЖЕНК :

$timestampz=time();

function generateRandomString($length = 60) {
    $characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
    $charactersLength = strlen($characters);
    $randomString = '';
    for ($i = 0; $i < $length; $i++) {
        $randomString .= $characters[rand(0, $charactersLength - 1)];
    }
    return $randomString;
}


$tokenparta = generateRandomString();


$token = $timestampz*3 . $tokenparta;

echo $token;

Зауважте, що я помножив часову позначку на три, щоб створити плутанину для того, хто користувач може задатися питанням, як генерується цей маркер;)

Я сподіваюся, що це допомагає :)


1
"Зверніть увагу, що я помножив часову позначку на три, щоб створити плутанину для того, хто може, хтось із користувачів може задатися питанням про те, як генерується цей маркер" Звучить як безпека через обфузацію stackoverflow.com/questions/533965/…
Гаррі

Це не "безпека через обфускування", оскільки часова марка потім додається до випадкової рядка, яка була сформована, і це фактична остаточна рядок, яку ми повертаємо;)
Kaszoni Ferencz


0

ми можемо використовувати цей два рядки коду для створення унікальних рядків, які протестували близько 10000000 разів ітерації

  $sffledStr= str_shuffle('abscdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890!@#$%^&*()_-+');
    $uniqueString = md5(time().$sffledStr);

0

Я завжди використовую цю функцію, щоб створити власну випадкову буквено-цифрову рядок ... Сподіваюся, це допоможе.

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

Сподіваюся, що це те, що ви шукаєте ...


0

Просте рішення - перетворити базу 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);
}


-1

Я вважаю, що проблема всіх існуючих ідей полягає в тому, що вони, мабуть, унікальні, але не однозначно унікальні (на що вказував у відповіді Даріуш Вальчак на лолетех). У мене є рішення, яке насправді є унікальним. Це вимагає, щоб ваш сценарій мав якусь пам’ять. Для мене це база даних SQL. Ви також можете просто написати десь у файл. Є дві реалізації:

Перший метод: мати ДВА поля, а не 1, які забезпечують унікальність. Перше поле - це ідентифікаційний номер, який не є випадковим, але є унікальним (Перше ідентифікатор - 1, друге 2 ...). Якщо ви використовуєте SQL, просто визначте поле ідентифікатора зі властивістю AUTO_INCREMENT. Друге поле не є унікальним, але є випадковим. Це можна створити за допомогою будь-якої іншої техніки, про яку люди вже згадували. Ідея Скотта була хорошою, але md5 зручний і, напевно, досить хороший для більшості цілей:

$random_token = md5($_SERVER['HTTP_USER_AGENT'] . time());

Другий метод: В основному та сама ідея, але спочатку виберіть максимальну кількість рядків, які коли-небудь будуть генеровані. Це може бути просто велика кількість, як трильйон. Потім зробіть те ж саме, згенеруйте ідентифікатор, але нульовий пробіл, щоб усі ідентифікатори мали однакову кількість цифр. Тоді просто з'єднайте ідентифікатор зі випадковою рядком. Він буде досить випадковим для більшості цілей, але розділ ідентифікатора забезпечить його унікальність.


-1

Ви можете використовувати цей код, я сподіваюся, він буде корисним для вас.

function rand_code($len)
{
 $min_lenght= 0;
 $max_lenght = 100;
 $bigL = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
 $smallL = "abcdefghijklmnopqrstuvwxyz";
 $number = "0123456789";
 $bigB = str_shuffle($bigL);
 $smallS = str_shuffle($smallL);
 $numberS = str_shuffle($number);
 $subA = substr($bigB,0,5);
 $subB = substr($bigB,6,5);
 $subC = substr($bigB,10,5);
 $subD = substr($smallS,0,5);
 $subE = substr($smallS,6,5);
 $subF = substr($smallS,10,5);
 $subG = substr($numberS,0,5);
 $subH = substr($numberS,6,5);
 $subI = substr($numberS,10,5);
 $RandCode1 = str_shuffle($subA.$subD.$subB.$subF.$subC.$subE);
 $RandCode2 = str_shuffle($RandCode1);
 $RandCode = $RandCode1.$RandCode2;
 if ($len>$min_lenght && $len<$max_lenght)
 {
 $CodeEX = substr($RandCode,0,$len);
 }
 else
 {
 $CodeEX = $RandCode;
 }
 return $CodeEX;
}

Деталі про генератор випадкових кодів у PHP


-2

Спрощення коду Скоттса вище, видаливши зайві петлі, які погано сповільнюються і не роблять його більш безпечним, ніж виклик openssl_random_pseudo_bytes лише один раз

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
 $rnd = hexdec(bin2hex(openssl_random_pseudo_bytes($bytes)));
 return $min + $rnd%$range;
}

function getToken($length)
{
 return bin2hex(openssl_random_pseudo_bytes($length)
}

На жаль, я не можу погодитися з вами, що це відповідне спрощення функції crypto_rand_secure. Цикл необхідний, щоб уникнути зміщення модуля, що виникає, коли ви берете результат випадково генерованого числа, а потім модифікуєте його на число, яке дійсно ділить діапазон. en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle#Modulo_bias . Використовуючи цикл для викидання будь-яких чисел поза діапазону та вибору нового числа, це дозволяє уникнути цього зміщення. stackoverflow.com/questions/10984974/…
Скотт

@Scott, це повідомлення про rand (). Однак у цьому випадку про це вже подбає openssl_random_pseudo_bytes, тому цикл стає непотрібним.
mabel

1
gist.github.com/anonymous/87e3ae6fd3320447f46b973e75c2ea85 - Будь ласка, запустіть цей сценарій. Ви побачите, що розподіл результатів не випадковий. Числа між 0-55 мають більшу кількість випадків, ніж числа між 56-100. opensslне викликає цього, це через зміщення модуля у вашій заяві про повернення. 100 (діапазон) не ділить 255 (1 байт) рівномірно і викликає ці результати. Моя найбільша проблема у вашій відповіді полягає в тому, що ви заявляєте, що функція настільки ж захищена, як і використання циклу, який є помилковим. Ваша реалізація crypto_rand_secure не є криптографічно захищеною та вводить в оману.
Скотт

@Scott, ми робимо відворот тут, справа не в останньому операторі повернення, він також може бути прогресивно обчислений із поверненого значення openssl_random_pseudo_bytes без використання модуля. Я зазначаю, що цикл є непотрібним, коли openssl_random_pseudo_bytes повертає випадкові байти. Роблячи цикл, ви не робите його більш безпечним, просто робите це повільно.
mabel

2
Ця відповідь поширює погану інформацію. Ви впевнені, що цикл не робить opensslбільш захищеним, але що важливіше, це не робить його менш безпечним, ця відповідь робить. Код, що знаходиться вище, бере ідеально хороше випадкове число і маніпулює ним шляхом зміщення його до менших чисел. Якщо ви поставите відмову у відповіді, вказуючи, що вона швидша (шляхом вилучення циклу), але це не CSPRNG, це вирішило б це питання. Будь ласка, з любов'ю до криптовалюти повідомте користувачів, що вихід цієї функції може бути упередженим і не є рівноцінним рішенням. Петля не просто там, щоб вас дратувати.
Скотт
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.