Прогнозування виходу ранду PHP ()


21

Я читав у численних джерелах, що вихід rand () PHP передбачуваний як його PRNG, і я здебільшого сприймаю це як факт просто тому, що я його бачив у дуже багатьох місцях.

Мене цікавить доказова концепція: як би я пішов про прогнозування виходу rand ()? Прочитавши цю статтю, я зрозумів, що випадкове число - це число, повернене зі списку, що починається з вказівника (насіння), - але я не можу уявити, як це передбачувано.

Чи може хтось розумно зрозуміти, який випадковий номер # генерується через rand () в даний момент часу протягом кількох тисяч здогадок? чи навіть 10000 здогадів? Як?

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


"але я не уявляю, як це передбачувано"? Вам потрібно спочатку прочитати на " en.wikipedia.org/wiki/Linear_congruential_generator, щоб ви могли почати уявляти, як це передбачувано. Потім ви можете переглянути своє питання, щоб усунути здивування та перейти до більш практичних питань зворотної інженерії PHP джерело функції rand, щоб побачити, як це працює.
С.Лотт

"Я припускав, що це потенційна дірка безпеки"? Тільки якщо Злий хакер міг отримати випадковий пароль користувача, використовуйте таблицю веселки, щоб скасувати хеш MD5, щоб відновити вихідне (попереднє хеш) значення, а потім гарантувати, що вони зробили наступний запит пароля. Теоретично можливо, я гадаю. Але лише в тому випадку, якщо вони мали робочу таблицю веселки для випадкового числа.
С.Лотт

@ S.Lott - це не питання пароля. Система дозволяє скинути пароль та надсилає вам електронний лист, який використовується в URL-адресі. Маркер генерується через MD5 (rand ()). Якщо ви можете передбачити вихід rand (), ви можете змінити будь-який пароль, не маючи хеша для оригіналу або не знаючи оригіналу.
Ерік

@Erik. Правильно. Замініть "випадковий пароль" на "випадковий маркер", якщо це допомагає. Маркером можна зловживати лише тоді, коли хтось зможе розкрутити хеш MD5 для відновлення випадкового числа І запевнити, що отримає наступне випадкове число. Прогнозування наступного ранду - це лише одна невелика частина. Скасування MD5 є важкою частиною.
С.Лотт

1
Зауважте, що MD5 (rand ()) має лише таку ж безпеку, що і rand (). Практично створити таблицю пошуку MD5 (rand ()) -> rand () для дуже обмеженого набору числа. З обмеженим доменом rand () ви можете спробувати просту грубу силу, якщо не існує механізму, який запобігає повторним спробам.
MZB

Відповіді:


28

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


Дякую Чарльзу - між Вашою відповіддю та читанням посилання на генератор лінійної конгруенції з Tangurena я відчуваю, що я краще зрозумію це. Я вже "знав", що використовувати rand () таким чином було поганою ідеєю, але я знаю, чому .
Ерік

Нічого, реквізит за ретельно прописану відповідь, дякую!
Девід Хобс

10

Щоб наочно проілюструвати, як rand()функція невипадкова , ось ось зображення, де всі пікселі зроблені з "випадкових" червоних, зелених та синіх значень:

Випадкові значення RGB

На зображеннях зазвичай не повинно бути жодного малюнка.

Я спробував дзвонити srand()з різними значеннями, це не змінює настільки передбачувану функцію.

Зауважте, що обидва не криптографічно захищені і не дають передбачуваних результатів.


7

вихід ранду PHP () передбачуваний як його PRNG

Це генератор лінійної конгруенції . Це означає , що у вас є функція, яка ефективно: NEW_NUMBER = (A * OLD_NUMBER + B) MOD C. Якщо графік NEW_NUMBER проти OLD_NUMBER ви почнете бачити діагональні лінії. Деякі примітки до документації щодо RAND PHP дають приклади того, як це зробити.

Це з'являється, тому що я побачив авторську бібліотеку, яка використовує rand () для створення маркера для користувачів, які втратили паролі, і я припустив, що це потенційна дірка в безпеці.

На машині Windows максимальне значення RAND становить 2 ^ 15. Це дає зловмиснику лише 32 768 можливостей перевірити.

Чи може хтось розумно зрозуміти, який випадковий номер # генерується через rand () в даний момент часу протягом кількох тисяч здогадок? чи навіть 10000 здогадів? Як?

Хоча ця стаття не є саме тією, яку ви шукаєте, вона показує, як деякі дослідники взяли існуючу реалізацію генератора випадкових чисел і використовували її для заробітку на техаському холдемі. Є 52! Можливо перетасувати колоди, але реалізація використовувала 32-розрядний генератор випадкових чисел (що є максимальним числом з mt_getrandmax на машині Windows) і закладає його з часом у мілісекундах з півночі. Це зменшило кількість можливих перетасованих колод з приблизно 2 ^ 226 до приблизно 2 ^ 27, що дозволило здійснити пошук у режимі реального часу та знати, якою колодою було розроблено.

Зробивши це, я зрозумів, що якби я заглядав зовні, я б не мав уявлення, як відгадати маркер, навіть знаючи, що це md5 rand ().

Я б рекомендував використовувати щось в сім'ї SHA-2, оскільки федери вважають md5 розбитим. Деякі люди використовують Google для розшифрування хедів md5, оскільки вони такі поширені. Просто хеш-щось потім перекиньте хеш на пошук Google - в основному Google став величезною веселковою таблицею .


1

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


1
Я думаю, що наступне число є повністю детермінованим. Не "відносно", але абсолютно. Проблема з генераторами псевдовипадкових чисел полягає в тому, що послідовність пройде статистичні тести. Два суміжних числа, хоча і повністю детерміновані, матимуть статистичні властивості, спільні з фактичними випадковими числами.
С.Лотт

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

@ S.Lott - У мене склалося враження, що число може з'являтися кілька разів у 2 ^ 32 можливих вихідних даних і щоразу, коли воно з'являється, може супроводжуватися різним числом. Але з огляду на насіння X, повертаючи результат Y, наступний результат завжди буде однаковим. Таким чином, на практиці може бути кілька цифр, які слідують за Y. Я, можливо, помиляюся; минуло давно, як я справді дивився на PRNG.
pdr
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.