Здатність відгадувати наступне значення 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.