Наскільки унікальним є uniqid?


76

Це питання насправді не є проблемою пошуку рішення, це більше питання простої цікавості. Функція PHP uniqid має більш ентропійний прапорець, щоб зробити вихід "більш унікальним". Це змусило мене задуматися, наскільки ймовірно, що ця функція дасть однаковий результат більше одного разу, коли more_entropy є істинним, порівняно з тим, коли це не так. Іншими словами, наскільки унікальним є uniqid, коли активовано функцію more_entropy, порівняно з тим, коли вона вимкнена? Чи є якісь недоліки постійного ввімкнення more_entropy?


3
Якщо ви хочете що - небудь завжди унікальний, вам необхідно реалізувати GUID . Майже будь-що інше врешті-решт зіткнеться, оскільки у функції лише стільки ентропії. Наприклад, uniqidз more_entropyмножиною дає лише близько 92 біт ентропії (23 гексбіти). Щоб зрозуміти, чому це не годиться для унікальності, див . Проблема з днем ​​народження ...
ircmaxell

@ircmaxell дякую за вказівку на проблему дня народження, це досить цікаво. Це слід обов’язково зазначити у відповіді.
Петр Пеллер,

2
uniqid () не є хеш-функцією, тому проблема з днем ​​народження не застосовується до неї. Однак він має свої вразливі місця.
Джоел Меллон,

@ircmaxell звідки це число? more_entropyстановить близько 30 біт ентропії (дев'ять десяткових цифр), мікросекундна частина - близько 20 (шість десяткових цифр), звідки береться решта? Вам потрібно було б вибрати другу з 100 000-річного діапазону, щоб отримати 42 біти ентропії.
Tgr

Відповіді:


36

Оновлення, березень 2014 року:

По-перше, важливо зауважити, що uniqidце трохи помилково, оскільки це не гарантує унікальний ідентифікатор.

Відповідно до документації PHP :

УВАГА!

Ця функція не створює випадкових або непередбачуваних рядків. Цю функцію не можна використовувати в цілях безпеки. Використовуйте криптографічно захищені випадкові функції / генератори та криптографічно захищені хеш-функції для створення непередбачуваного захищеного ідентифікатора.

І

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


Встановлення більш ентропії в true генерує більш унікальне значення, однак час виконання довший (хоча і в незначній мірі), згідно з документами:

Якщо встановити значення TRUE, uniqid () додасть додаткову ентропію (за допомогою комбінованого лінійного конгруентного генератора) в кінці поверненого значення, що збільшує ймовірність того, що результат буде унікальним.

Зверніть увагу на рядок, increases the likelihood that the result will be uniqueа не той, що є гарантом унікальності.

Ви можете "нескінченно" прагнути до унікальності, до певної міри, і вдосконалювати, використовуючи будь-яку кількість процедур шифрування, додавання солей тощо - це залежить від мети.

Я рекомендую переглянути коментарі до основної теми PHP, зокрема:

http://www.php.net/manual/en/function.uniqid.php#96898

http://www.php.net/manual/en/function.uniqid.php#96549

http://www.php.net/manual/en/function.uniqid.php#95001

Я б порадив розібратися, навіщо потрібна унікальність, це для безпеки (тобто для додавання до процедури шифрування / скремблювання)? Крім того, наскільки унікальним він повинен бути? Нарешті, подивіться на врахування швидкості. Придатність буде змінюватися з урахуванням основних міркувань.


1
Найважливіший урок з цими коментарями до функцій - це те, що uuid сам по собі є дуже небезпечним ідентифікатором, який передається як ідентифікатор, що читається файлом cookie / клієнтом, але як локальний / захищений унікальний ідентифікатор має кілька хороших застосувань, а саме швидкість. 2,5 цента.
DrPerdix

3
Я не знаю, чи це ще було очевидно, але не використовуйте uniqid(або це похідні) для чогось, що стосується безпеки. PHP пропонує цілу купу криптографічних безпечних випадкових генераторів, таких як: openssl_random_pseudo_bytes. Будь ласка, використовуйте відповідний інструмент для роботи.
Halcyon

1
Якщо припустити, що не збережено 2 файли за одну і ту ж мікросекунду, мікс мікросекунди мітки часу буде унікальним для кожного файлу.
CMCDragonkai

Статистично малоймовірно, що ви зіткнетесь, але не неможливо. Помістіть своє покоління uniqid всередину do{} while(collision). Я використовую цей підхід, наприклад, під час створення шляхів для завантажених файлів.
afilina

2
Не знаю, чому цю відповідь прийняли. Унікальний! = Випадковий / непередбачуваний
гаделат

16

Речі унікальні лише в тому випадку, якщо ви перевірите, що вони ще не існують. Не має значення, яку функцію ви використовуєте для створення "випадкового" рядка або ідентифікатора - якщо ви не переконайтесь, що це не дублікат, то завжди є така можливість;;)

Хоча uniqid базується на поточному часі, попереджувальна примітка все ще застосовується - це лише залежить від того, де ви будете використовувати ці "унікальні ідентифікатори". Підказка до всього цього полягає там, де сказано "більш унікальний". Унікальний є унікальний є унікальний. Як можна отримати щось більш-менш унікальне, мене трохи бентежить!

Перевірка, як зазначено вище, та поєднання всього цього дозволить вам отримати щось наближене до унікальності, але все це відносно місця використання ключів та контексту. Сподіваюся, це допоможе!


10
Існує величезна різниця між "шанс отримати зіткнення - одна з десяти тисяч" і "зміна рівня зіткнення менша, ніж кожен користувач програми, який одночасно потрапляє під удар блискавки". 128-бітове значення, яке генерується хорошим RNG з хорошим насінням, настільки близьке до того, щоб бути «справді» унікальним, що не має значення, враховуючи неймовірно високі витрати на отримання чогось доказово (і непередбачувано) унікального.
Майкл Борґвардт,

6
Тільки для продовження вашої точки зору @Michael: Для 128 біт вам потрібно було б, щоб усі в США (300 мільйонів) генерували 1 мільйон чисел в секунду приблизно протягом доби, щоб отримати 50% ймовірність зіткнення ... Для 512 бітів, вам знадобиться кожне тіло на землі (7 мільярдів людей), щоб генерувати по 1 трильйон чисел в секунду кожне протягом наступних 10^47років, лише щоб мати 50% ймовірність зіткнення ... Так що так, при досить великій верхній межі на випадковому числі І досить хорошому RNG, ви можете імітувати унікальність лише випадковістю ...
ircmaxell

1
Я повністю погоджуюсь з вашими прикладами ідеального світу, як зазначено вище. Шанси мінімальні. Однак випадковість не є ідеальною у реалізаціях, згаданих у вихідному питанні, і я вважаю, що домен, де використовується цей унікальний номер, є важливим. Якщо у вас було 1000 серверів, кожен з яких робив "унікальні" ідентифікатори, засновані на мікрочасах, і припускаючи, що вони були унікальними "просто тому", то в якийсь момент ви цілком можете отримати опік. Ігноруючи будь-які дивацтва в коді .. помилок, або що завгодно. Тут різниця між реальністю та теорією, і тому ми перевіряємо;)
dmp

5
"Принцип генерування невеликої кількості кінцевої неймовірності шляхом простого підключення логічних схем Bambleweeny 57 Sub-Meson Brain до атомного векторного плоттера, підвішеного в сильному продюсері Brownian Motion (скажімо, гарячої гарячої чашки чаю), звичайно, був добре зрозумілий . "
dmp

1
@ircmaxell: Суть у тому, що ці цифри вимагають реальної випадковості, а отже і справжнього СПГ. Ви навіть не змогли змоделювати його за допомогою PRNG із внутрішнім станом> 128 біт, якщо у вас також не було способу розподілити його унікальним / випадковим значенням> 128 біт. Але це сама проблема, яку ви повинні вирішити! І все, що менше цього, практично гарантує зіткнення. Ті самі 300 мільйонів людей, якби вони використовували поганий запас свого компілятора rand(), мали б> 90% ймовірності зіткнення на першій ітерації . Крім того, якщо вам потрібна унікальність, навіть шанс зіткнення на 0,001% занадто великий.
cHao

10

З дискусій про функцію на сайті керівництва PHP:

Як зазначають інші нижче, без префікса та без "доданої ентропії" ця функція просто повертає мітку часу UNIX з доданим мікросекундним лічильником як шістнадцяткове число; це більш-менш просто мікрочас () у формі hexit.

[...]

Також варто зауважити, що оскільки microtime () працює лише в системах, які мають gettimeofday ()> present, яких Windows спочатку НЕ МОЖЕ, uniqid () може дати лише часову мітку UNIX з роздільною здатністю в середовищі Windows.

Іншими словами, без "more_entropy" функція абсолютно жахлива і ніколи не повинна використовуватися, точка. Кодуючи документацію, прапор використовуватиме "комбінований лінійний конгруентний генератор" для "додавання ентропії". Ну, це досить слабкий RNG. Тому я повністю пропустив би цю функцію і використав би щось на основі mt_rand з хорошим насінням для речей, що не стосуються безпеки, і SHA-256 для речей, які є.


7

Без прапора more_unique він повертає мітку часу unix з лічильником мікросекунд, тому, якщо два виклики здійснюються в одну і ту ж мікросекунду, вони повернуть той самий "унікальний" ідентифікатор.

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


21
Вірте чи ні, насправді це називає usleep (1), щоб переконатися, що цього ніколи не відбувається!
Елі

2
@Eli не впевнений, тролінг чи ні, але очевидно, що це не так, оскільки я отримую дублікати, що запускають це: for ($ i = 0; $ i <10; $ i ++) echo uniqid (). "\ n";
djule5,

3
@ djule5 Ні, не тролінг: github.com/php/php-src/blob/ ... Ви, можливо, використовуєте дуже стару версію PHP або на платформі usleep не існує?
Елі,

@Eli цікаво ха-ха дякую за джерело! Я використовую PHP 5.5.11, але я працюю в Windows на цій машині розробника ... так що, мабуть, це пояснюється! Тож тоді це точно не настільки унікально для Windows ...
djule5

1
@ user5542121 вони вирішили не викликати usleep та час опитування, оскільки usleep "може змусити ядро ​​запланувати інший процес, спричиняючи паузу близько 10 мс" ~ github.com/php/php-src/blob/PHP-7.2.12/ доб / стандарт /…
x3ns

5

Відповідним бітом вихідного коду є

if (more_entropy) {
    uniqid = strpprintf(0, "%s%08x%05x%.8F", prefix, sec, usec, php_combined_lcg() * 10);
} else {
    uniqid = strpprintf(0, "%s%08x%05x", prefix, sec, usec);
}

Тож more_entropyдодає дев’ять дещо випадкових десяткових цифр ( php_combined_lcg()повертає значення в (0,1)) - це 29,9 біт ентропії, вершини (насправді, мабуть, менше, оскільки LCG не є криптографічно захищеним генератором псевдовипадкових чисел).

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.