Здатність відгадувати наступне значення randзв'язана з можливістю визначити, з чим srandвикликали. Зокрема, посів srandзаздалегідь визначеного числа призводить до передбачуваного виходу ! З інтерактивного запиту PHP:
[charles@charles-workstation ~]$ php -a
Interactive shell
php > srand(1024);
php > echo rand(1, 100);
97
php > echo rand(1, 100);
97
php > echo rand(1, 100);
39
php > echo rand(1, 100);
77
php > echo rand(1, 100);
93
php > srand(1024);
php > echo rand(1, 100);
97
php > echo rand(1, 100);
97
php > echo rand(1, 100);
39
php > echo rand(1, 100);
77
php > echo rand(1, 100);
93
php >
Це не просто якась флейка. Більшість версій PHP * на більшості платформ ** будуть генерувати послідовності 97, 97, 39, 77, 93, коли srand'd з 1024.
Щоб було зрозуміло, це не проблема PHP, це проблема самої реалізації rand. Ця ж проблема виникає і в інших мовах, які використовують ту саму (або подібну) реалізацію, включаючи Perl.
Хитрість полягає в тому, що будь-яка розумна версія PHP матиме попереднє засідання srandз "невідомим" значенням. О, але це насправді невідомо. Від ext/standard/php_rand.h:
#define GENERATE_SEED() (((long) (time(0) * getpid())) ^ ((long) (1000000.0 * php_combined_lcg(TSRMLS_C))))
Отже, це деяка математика time(), PID та результат php_combined_lcg, який визначено в ext/standard/lcg.c. Я не збираюсь тут робити с & р, як, ну, очі засклили, і я вирішив припинити полювання.
Трохи Googling показує, що інші області PHP не мають найкращих властивостей генерації випадкових випадків , і закликає php_combined_lcgвиділитися тут, особливо цей фрагмент аналізу:
Ця функція не тільки gettimeofdayповертає нам точну мітку часу на срібному блюді, але й додає у висновку LCG, якщо ми вимагаємо "більше ентропії" (від PHP uniqid).
Так, цеuniqid . Здається, що значення - php_combined_lcgце те, що ми бачимо, коли ми дивимося на отримані шістнадцяткові цифри після виклику uniqidз другим аргументом, встановленим на справжнє значення.
Тепер, де ми були?
О, так. srand.
Отже, якщо код, з якого ви намагаєтеся передбачити випадкові значення , не викликає srand, вам потрібно буде визначити значення php_combined_lcg, яке ви можете отримати (опосередковано?) За допомогою виклику uniqid. Маючи це значення в руці, можна ретельно застосувати решту значень - time(), PID та деяку математику. Пов’язане питання безпеки стосується розбиття сеансів, але та сама техніка працювала б і тут. Знову із статті:
Ось підсумок кроків нападу, описаних вище:
- зачекайте, поки сервер перезавантажиться
- отримати значення uniqid
- груба сила з цього насіння РНГ
- опитувати статус в Інтернеті, щоб чекати появи мети
- перемежовувати опитування статусу з uniqid-опитуваннями для відстеження поточного часу сервера та значення RNG
- Ідентифікатор сеансу грубої сили проти сервера, використовуючи час та інтервал значень RNG, встановлений при опитуванні
Просто замініть цей останній крок за потребою.
(Про це питання безпеки повідомлялося в попередній версії PHP (5.3.2), ніж у нас зараз (5.3.6), тому можливо, що поведінка uniqidта / або php_combined_lcgзмінилася, тому ця специфічна методика більше не може бути реалізованою. YMMV.)
З іншого боку, якщо код, який ви намагаєтеся вручити дзвінки srandвручну , тоді, якщо вони не використовують щось у багато разів краще, ніж результат php_combined_lcg, вам, ймовірно, доведеться набагато простіше вгадати значення та висіяти місцевий генератор з потрібним номером. Більшість людей, які б дзвонили вручну, srandтакож не усвідомлювали, наскільки це жахлива ідея, і, отже, навряд чи будуть використовувати кращі значення.
Варто зазначити, що mt_randтакож страждає та ж проблема. Посів mt_srandз відомим значенням також дасть передбачувані результати. Виключення ентропії openssl_random_pseudo_bytes, можливо, є більш безпечним.
tl; dr: Для найкращих результатів не сійте генератор випадкових чисел PHP, і заради блага, не піддавайте uniqidкористувачам. Виконання будь-якого або обох цих випадків може призвести до того, що ваші випадкові числа будуть більш зрозумілими.
Оновлення для PHP 7:
PHP 7.0 вводить random_bytesі random_intяк основні функції. Вони використовують реалізацію основної системи CSPRNG, що позбавляє їх від проблем, що виникають у насінньому генераторі випадкових чисел. Вони ефективно схожі openssl_random_pseudo_bytes, лише без необхідності встановлення розширення. Для PHP5 доступний поліфактор .
*: Патч безпеки Suhosin змінює поведінку randта mt_randтаке, що вони завжди повторюються при кожному дзвінку. Сухосін надається третьою стороною. Деякі дистрибутиви Linux за замовчуванням включають його в свої офіційні пакети PHP, а інші роблять це опцією, а інші повністю ігнорують його.
**: Залежно від платформи та базових бібліотечних викликів, що використовуються, будуть генеруватися різні послідовності, ніж тут зафіксовано, але результати все одно повинні повторюватися, якщо не використовується патч Suhosin.