Сбор энтропии в веб-приложениях для создания (более) безопасных случайных чисел

после нескольких дней исследований и обсуждений я придумал этот метод для сбора энтропии посетителей (вы можете увидеть историю моего исследования здесь)

когда пользователь посещает, я запускаю этот код:

$entropy=sha1(microtime().$pepper.$_SERVER['REMOTE_ADDR'].$_SERVER['REMOTE_PORT'].
$_SERVER['HTTP_USER_AGENT'].serialize($_POST).serialize($_GET).serialize($_COOKIE)); 

примечание: перец — это случайная строка для каждого сайта/установки, заданная вручную.

затем я выполняю следующий (My) SQL-запрос:

$query="update `crypto` set `value`=sha1(concat(`value`, '$entropy')) where name='entropy'";

это означает, что мы объединяем энтропию запроса посетителя с уже собранными другими.

это все.

затем, когда мы хотим сгенерировать случайные числа, мы объединяем собранную энтропию с выходом:

$query="select `value` from `crypto` where `name`='entropy'";
//...
extract(unpack('Nrandom', pack('H*', sha1(mt_rand(0, 0x7FFFFFFF).$entropy.microtime())))); 

примечание: последняя строка является частью модифицированной версии функции crypt_rand из phpseclib.

расскажите, пожалуйста, ваше мнение о схеме и другие идеи/информацию о сборе энтропии/генерации случайных чисел.

PS: я знаю об источниках случайности, таких как /dev/urandom. эта система является просто вспомогательной системой или (когда у нас нет (доступа) к этим источникам) резервной схемой.


person H M    schedule 27.03.2012    source источник
comment
Есть конкретный вопрос?   -  person Burhan Khalid    schedule 27.03.2012
comment
Это действительно относится к обзору кода, потому что он требует оценки фрагмента кода, а не помощи в решении конкретной проблемы.   -  person GordonM    schedule 27.03.2012
comment
Создание собственной криптовалюты обычно является плохой идеей. Источники случайности, такие как /dev/random и /dev/urandom, уже делают это, и делают это правильно, и они осведомлены о таких вещах, как количество энтропии, которое у них есть. Резервная схема может быть плохой идеей, потому что она может скрыть серьезные проблемы с вашей системой.   -  person Nick Johnson    schedule 27.03.2012
comment
Одним из основных свойств алгоритмов генерации хэшей является то, что SHA1 необратим. Таким образом, ваш код не добавляет безопасности по сравнению с этим: update crypto` set value=sha1(concat(value, DATE()))) где name='entropy'`. Это происходит потому, что SHA1 (разработанный как) является спорным.   -  person Mark Huk    schedule 27.03.2012
comment
DATE() легче угадать, чем concat(value, DATE()). Несколько атак против PHP mt_srand() используют это в своих интересах. PID также не являются достаточно случайными, но PHP их использует.   -  person neubert    schedule 27.03.2012
comment
Хорошо, я отзываю свои заявления и вместо этого начинаю награду за неправильный ответ. Моя вина. Дополнительная информация здесь< /а>   -  person Maarten Bodewes    schedule 01.04.2012
comment
Как вы тестировали встроенные методы? Как вы проверяли свои улучшения? Каков был результат?   -  person Mike Sherrill 'Cat Recall'    schedule 01.04.2012


Ответы (8)


В лучшем случае самой большой опасностью для вас будет раскрытие информации локальным пользователем. В худшем случае весь мир сможет предсказать ваши данные. Любой пользователь, имеющий доступ к тем же ресурсам, что и вы: к тем же файлам журналов, тем же сетевым устройствам, тому же пограничному шлюзу или той же линии, которая работает между вами и вашими удаленными соединениями позволяет им прослушивать ваш трафик, раскручивая ваш генератор случайных чисел.

Как бы они это сделали? Да ведь базовое применение теории информации и немного знания из криптография, конечно!

Однако вы не ошиблись! Заполнение вашего PRNG реальными источниками случайности, как правило, весьма полезно для предотвращения вышеуказанных атак. Например, этот же уровень атаки может быть использован кем-то, кто понимает, как /dev/random заполняется на для каждой системы, если система имеет низкую энтропию или ее источники случайности воспроизводимы.

Если вы можете достаточно защитить процессы, которые заполняют ваш пул энтропии (например, собирая данные из нескольких источников по защищенным линиям), вероятность того, что кто-то сможет их подслушать, становится все меньше и меньше по мере того, как вы все ближе и ближе приближаетесь к желаемому. криптографические качества одноразового блокнота.

Другими словами, не делайте этого в PHP, используя один источник случайности, подаваемый в один вихрь Мерсенна. Сделайте это правильно, читая из ваших лучших, системных конкретная альтернатива /dev/random, засевающая его пул энтропии из как можно большего количества безопасных, различных источников «истинной» случайности. Я понимаю, что вы заявили, что эти источники случайности недоступны, но это понятие странно, когда подобные функции предоставляются всем основным операционным системам. Итак, полагаю, я нахожу концепцию «вспомогательной системы» в данном контексте сомнительной.

Это по-прежнему будет уязвимо для атаки со стороны локального пользователя, осведомленного о ваших источниках энтропии, но защита машины и увеличение истинной энтропии внутри /dev/random значительно усложнит им выполнение своей грязной работы, кроме атака "человек посередине".

Что касается случаев, когда /dev/random действительно доступен, вы можете довольно легко заполнить его:

  • Посмотрите, какие варианты существуют в вашей системе для использования /dev/hw_random.
  • Используйте rngd (или хорошую альтернативу) для определения источников случайности.
  • Используйте rng-tools за проверку и улучшение вашего профиля случайности
  • И, наконец, если вам нужен хороший и надежный источник случайных чисел, подумайте об инвестировании в более специализированное оборудование.

Желаем удачи в защите вашего приложения.


PS: Вы можете задать подобные вопросы на Security.SE и Cryptography.SE в будущем!

person MrGomez    schedule 31.03.2012
comment
Можно предположить, что /dev/(u)random или openssl недоступны, тем более, что автор буквально прописал это в вопросе. Создание безопасного случайного источника, если таковые имеются, было бы бесполезным делом. - person Maarten Bodewes; 02.04.2012
comment
@owlstead Ах! Простите мой сумасброд. Дело в том, что в основе ваших криптографических функций всегда должен быть какой-то источник случайности. Хотя можно было бы выбрать лучший алгоритм (например, Blum Blum Shub), у вас все еще есть проблема с подслушиванием. если вы полагаетесь исключительно на сетевые тайминги для заполнения PRNG. Итак, я думаю, мне больше всего любопытно, почему источник случайности недоступен, даже в вспомогательном случае, когда он в той или иной форме предоставляется всем основным операционным системам. - person MrGomez; 02.04.2012
comment
Ну, может потому, что провайдер сервиса PHP не выставляет источники рандома. Проблема в том, что всегда доступны случайные источники random и mt_random, ни один из которых не является безопасным. Лично я предпочитаю Java на серверах, так как крипто PHP сильно не хватает во многих отделах. Аскер, кажется, знает об этом и хочет создать (хотя и неоптимальное) решение этой проблемы. - person Maarten Bodewes; 02.04.2012
comment
я пишу систему регистрации и входа в систему (планирую выпустить ее под лицензией GPL). я не чувствую необходимости в чрезвычайно безопасных случайных числах, потому что это просто обычное веб-приложение, работающее на обычном (возможно, общем) хостинге. но я видел, что безопасность mt_rand слишком низкая даже для обычных приложений/сценариев. я не вижу серьезной проблемы в автоматическом возврате к этой схеме, если более безопасные источники недоступны. моя функция rand представляет собой модифицированную версию crypt_rand из phpseclib, которая будет автоматически использовать эти лучшие источники, если они доступны (и моя дополнительная энтропия по-прежнему сочетается с их выводом с помощью sha1). - person H M; 02.04.2012
comment
@HM Спасибо за разъяснение! Думаю, исходя из ваших требований, я бы выкинул ваш Mersenne Twister (см. примечание по криптографии здесь) и используйте криптографически более совершенный шифр, например, Blum Blum Shub, тогда и только тогда, когда вы хотите оптимизировать сильную случайность. В любом случае, который вы выберете, я просто задокументирую и проинформирую своих пользователей о криптографических свойствах выбранной вами схемы. Я считаю, что, учитывая ваши требования, это лучшее, что вы можете сделать. :) - person MrGomez; 02.04.2012

Используйте Random.Org

Если вам нужны действительно случайные числа, используйте random.org. Эти числа генерируются с помощью атмосферного шума. Помимо библиотеки для PHP, он также имеет http://www.random.org/clients/http/, который позволяет простыми запросами получать действительно случайные числа:

https://www.random.org/integers/?num=10&min=1&max=6&col=1&base=10&format=plain&rnd=new

Это означает, что вы можете просто получить реальные случайные числа в PHP без каких-либо дополнительных расширений PECL на сервере.

Если вы не хотите, чтобы другие пользователи могли украсть ваши случайные числа (как утверждает MrGomez), просто используйте https с проверкой сертификата. Ниже приведен пример с проверкой сертификата https:

$url = "https://www.random.org/integers/?num=10&min=1&max=6&col=1&base=10&format=plain&rnd=new";
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);
$response = curl_exec($ch);
if ($response === FALSE)
        echo "http request failed: " . curl_error($ch);
else
        echo $response;    
curl_close($ch);

Если вам нужна дополнительная информация о том, как создавать https-запросы:

Подробнее о безопасности

Опять же, некоторые могут возразить, что если злоумышленник запрашивает random.org одновременно с вами, он может получить те же числа и предсказать... Я не знаю, будет ли работать random.org таким образом, но если вы действительно Вы можете уменьшить вероятность, обманув злоумышленника фиктивным запросом, который вы выбрасываете, или используя только определенную часть полученных вами случайных чисел.

Как отмечает в своем комментарии г-н Гомес, это следует рассматривать не как окончательное решение проблемы безопасности, а лишь как один из возможных источников энтропии.

Представление

Конечно, если вам нужна блиц-латентность, то делать один запрос random.org на один запрос клиента может быть не лучшей идеей... но как насчет того, чтобы просто сделать один больший запрос на предварительное кэширование случайных чисел каждые 5 минут?

person Tomas    schedule 02.04.2012
comment
Хорошая идея! Но они явно информируют людей не использовать его для криптографической секретности из-за утечки информации: Я должен вероятно, обратите внимание, что, хотя получение номеров через безопасный HTTP защитит их от наблюдения во время передачи, любой, кто действительно заботится о безопасности, не должен доверять кому-либо другому (включая RANDOM.ORG) создание своих криптографических ключей. -- Вы можете позвольте себе доверять им, но вы даете им ключи от каждой установки этого фреймворка. - person MrGomez; 05.04.2012
comment
Хорошая заметка, мистер Гомес, спасибо! Однако я бы не сказал, что мы даем им ключи. Вопрос был об увеличении энтропии, поэтому я полагаю, что random.org будет использоваться только как один из ее источников. Злоумышленник должен обладать ВСЕМИ этими источниками. В сфере безопасности всегда существует компромисс между вероятностью взлома и ценой решения. Random.org, хотя и не является окончательным решением проблемы энтропии, является очень дешевым источником энтропии (см. простое решение выше), и, добавляя его, вы можете только снизить вероятность взлома. - person Tomas; 07.04.2012

Чтобы перейти к сути, насколько я знаю, нет способа генерировать энтрофию внутри PHP-скрипта, извините за этот неответ. Даже если вы посмотрите на хорошо настроенные скрипты, такие как phppass, вы увидите, что их резервная система не может выполнять некоторые магия.

Вопрос в том, стоит ли вообще пробовать или нет. Поскольку вы хотите опубликовать свою систему под лицензией GPL, вы, вероятно, не знаете, в каком сценарии она будет использоваться. На мой взгляд, тогда лучше требовать случайный источник или быстро терпеть неудачу (умирать с соответствующим сообщением об ошибке), чтобы разработчик, который хочет использовать вашу систему, сразу знал, что есть проблема .

Чтобы прочитать из случайного источника, вы можете вызвать функцию mcrypt_create_iv()...

$randomBinaryString = mcrypt_create_iv($length, MCRYPT_DEV_URANDOM);

...эта функция читает из случайного пула операционной системы. Начиная с PHP 5.3, он делает это и на серверах Windows, так что вы можете предоставить PHP для обработки случайного источника.

person martinstoeckli    schedule 07.04.2012

Если у вас есть доступ к /dev/urandom, вы можете использовать это:

function getRandData($length = 1024) {
    $randf  = fopen('/dev/urandom', 'r');
    $data   = fread($randf, $length);
    fclose($randf);
    return $data;
}

ОБНОВЛЕНИЕ: конечно, у вас должна быть резервная копия на случай, если открытие устройства не удастся

person Vlad Balmos    schedule 06.04.2012
comment
Он спрашивает о дизайне функции резервного копирования. - person Maarten Bodewes; 09.04.2012
comment
Просто на заметку: PHP будет буферизовать 8192 байта для fread() вызовов, нужно вам это или нет, что может привести к потере большого количества энтропии. Используйте stream_set_read_buffer($randf, 0);, чтобы отключить буферизацию после открытия дескриптора файла и до вызова fread(), чтобы избежать этого. - person Scott Arciszewski; 12.07.2015

если у вас есть доступ к клиентской части, вы можете включить отслеживание движения мыши — это то, что настоящий крипт использует для дополнительного уровня энтропии.

person jancha    schedule 07.04.2012
comment
Если вы не контролируете клиент и передачу (SSL), я бы с осторожностью полагался на что-либо подобное. - person Maarten Bodewes; 09.04.2012

как я уже говорил, моя функция rand представляет собой модифицированную версию функции crypt_random из phpseclib. Вы могли видеть это по ссылке, указанной в моем первом посте. по крайней мере это подтвердил автор криптографической библиотеки phpseclib; недостаточно для обычных приложений? я не говорю об экстремальной/теоретической безопасности, просто говорю о практической безопасности в той мере, в какой это действительно необходимо, и в то же время «легко»/«достаточно дешево», доступной почти для всех обычных приложений в Интернете.

crypt_random в phpseclib эффективно и незаметно возвращается к mt_rand (который, как вы должны знать, действительно слаб) в худшем случае (отсутствуют openssl_random_pseudo_bytes или urandom), но моя функция использует гораздо более безопасную схему в таких случаях. это просто откат к схеме, в которой перебор/предсказание его вывода намного сложнее и (должно быть) на практике достаточным для всех обычных приложений/сайтов. он использует возможную (на практике очень вероятную и трудно предсказуемую/обойденную) дополнительную энтропию, которая накапливается с течением времени, и которая быстро становится почти невозможной для посторонних. он добавляет эту возможную энтропию к выводу mt_rand (а также к выводу других источников: urandom, openssl_random_pseudo_bytes, mcrypt_create_iv). если вы проинформированы, вы должны знать, что эта энтропия может быть добавлена, но не вычтена. в (почти наверняка очень редком) худшем случае эта дополнительная энтропия будет равна 0 или слишком малой величине. в посредственном случае, который я думаю почти во всех случаях, это было бы даже больше, чем практически необходимо, я думаю. (у меня были обширные исследования в области криптографии, поэтому, когда я говорю, что думаю, это основано на гораздо более информированном и научном анализе, чем у обычных программистов).

см. полный код моего модифицированного crypt_random:

function crypt_random($min = 0, $max = 0x7FFFFFFF)
{
    if ($min == $max) {
        return $min;
    }

    global $entropy;

    if (function_exists('openssl_random_pseudo_bytes')) {
        // openssl_random_pseudo_bytes() is slow on windows per the following:
        // http://stackoverflow.com/questions/1940168/openssl-random-pseudo-bytes-is-slow-php
        if ((PHP_OS & "\xDF\xDF\xDF") !== 'WIN') { // PHP_OS & "\xDF\xDF\xDF" == strtoupper(substr(PHP_OS, 0, 3)), but a lot faster
            extract(unpack('Nrandom', pack('H*', sha1(openssl_random_pseudo_bytes(4).$entropy.microtime()))));
            return abs($random) % ($max - $min) + $min; 
        }
    }

    // see http://en.wikipedia.org/wiki//dev/random
    static $urandom = true;
    if ($urandom === true) {
        // Warning's will be output unles the error suppression operator is used.  Errors such as
        // "open_basedir restriction in effect", "Permission denied", "No such file or directory", etc.
        $urandom = @fopen('/dev/urandom', 'rb');
    }
    if (!is_bool($urandom)) {
        extract(unpack('Nrandom', pack('H*', sha1(fread($urandom, 4).$entropy.microtime()))));
        // say $min = 0 and $max = 3.  if we didn't do abs() then we could have stuff like this:
        // -4 % 3 + 0 = -1, even though -1 < $min
        return abs($random) % ($max - $min) + $min;
    }


    if(function_exists('mcrypt_create_iv') and version_compare(PHP_VERSION, '5.3.0', '>=')) {
        @$tmp16=mcrypt_create_iv(4, MCRYPT_DEV_URANDOM);
        if($tmp16!==false) {
            extract(unpack('Nrandom', pack('H*', sha1($tmp16.$entropy.microtime()))));
            return abs($random) % ($max - $min) + $min;
        }
    }


    /* Prior to PHP 4.2.0, mt_srand() had to be called before mt_rand() could be called.
       Prior to PHP 5.2.6, mt_rand()'s automatic seeding was subpar, as elaborated here:

       http://www.suspekt.org/2008/08/17/mt_srand-and-not-so-random-numbers/

       The seeding routine is pretty much ripped from PHP's own internal GENERATE_SEED() macro:

       http://svn.php.net/viewvc/php/php-src/tags/php_5_3_2/ext/standard/php_rand.h?view=markup */
    static $seeded;
    if (!isset($seeded) and version_compare(PHP_VERSION, '5.2.5', '<=')) { 
        $seeded = true;
        mt_srand(fmod(time() * getmypid(), 0x7FFFFFFF) ^ fmod(1000000 * lcg_value(), 0x7FFFFFFF));
    }

    extract(unpack('Nrandom', pack('H*', sha1(mt_rand(0, 0x7FFFFFFF).$entropy.microtime()))));
    return abs($random) % ($max - $min) + $min;

}

$entropy содержит мою дополнительную энтропию, которая получается из энтропии всех параметров запросов, объединенной до сих пор + энтропии параметров текущего запроса + энтропии случайной строки (*), заданной вручную во время установки.

*: длина: 22, состоит из строчных и прописных букв + цифр (более 128 бит энтропии)

person H M    schedule 10.04.2012

Обновление 2: Предупреждение о проверке кода для всех: не используйте код в исходном вопросе. Это ответственность за безопасность. Если этот код где-либо находится в сети, удалите его, так как он открывает всю систему, сеть и базу данных для недобросовестного пользователя. Вы не только раскрываете свой код, но и все данные своих пользователей.

Никогда не выполняйте сериализацию вводимых пользователем данных. Если вы уже делаете это в своем коде, остановите сервер и измените код. Это отличный пример того, как не заниматься криптовалютой самостоятельно.

Обновление 1: для обеспечения реальной безопасности вам необходимо, чтобы в вашей энтропии была неугадываемая случайная мешанина. Подходящим вариантом добавления энтропии, на который ссылается ваш вопрос, является использование дельты времени выполнения вашего скрипта Не microtime() отдельно . Потому что Delta зависит от загрузки вашего сервера. Так же как и комбинация аппаратной среды, температуры, нагрузки на сеть, нагрузки на питание, доступа к диску, использования процессора и колебаний напряжения, которые вместе являются непредсказуемыми.

Использование функции Time(), метки времени или микровремени является недостатком в вашей реализации.

Приходит код Delta Exemple выполнения скрипта:

@martinstoeckli правильно заявил, что подходящее случайное поколение для криптографии от

 mcrypt_create_iv($lengthinbytes, MCRYPT_DEV_URANDOM);

но не соответствует требованиям отсутствия криптомодуля

В SQL используйте RAND() в сочетании с вашим сгенерированным числом. http://www.tutorialspoint.com/mysql/mysql-rand-function.htm

Php также предлагает функцию Rand().

http://php.net/manual/en/function.rand.php

они не дадут вам один и тот же номер, чтобы вы могли использовать оба.

person GuruJR    schedule 07.04.2012
comment
Это не безопасные случайные функции, что было важно. - person Maarten Bodewes; 09.04.2012
comment
@owlstead mt_rand() просто быстрее, чем rand() Не более безопасный. mt_rand() менее безопасна, чем rand(). Поскольку распределение возвращаемых значений mt_rand() смещено в сторону четных чисел в 64-битных сборках PHP, когда max превышает 2^32. так что крипто-мудрый Rand() отстой меньше, чем mt_rand(). - person GuruJR; 09.04.2012
comment
@GuruJR - проблема в том, что эти генераторы псевдослучайных чисел дают предсказуемые результаты. Если вы запустите их с одним и тем же начальным числом (обычно временем), вы получите точно такой же результат. Вы можете думать о них как о математической функции, которая возвращает случайные числа со статистическими ограничениями. - person martinstoeckli; 09.04.2012
comment
@martinstoeckli я знаю, что это внутренняя проблема всех псевдослучайных генераций с компьютеров. Даже MCRYPT_DEV_URANDOM не является чистым choas, но, по крайней мере, пытается нормализовать свои результаты. Таким образом, семя, которое ему нужно использовать, должно быть семенем, о котором даже он сам не может догадаться, поэтому я предлагаю дельту времени выполнения его сценария. Использование любой временной метки является ошибкой в ​​реализации. - person GuruJR; 09.04.2012
comment
@GuruJR - Хорошо, я думаю, я понимаю, что вы имеете в виду, вы бы использовали два предсказуемых генератора случайных чисел, но у них будет два разных семени. Моей первой мыслью было, что объединение двух небезопасных функций не сделает безопасную функцию, но потом... я недостаточно опытен, чтобы судить об этом. Если злоумышленник попытается предварительно вычислить случайные значения набора начальных значений, тогда ему придется предварительно вычислить n ^ 2 значений. Позже вы добавили использовать время выполнения, это может дать реальную энтрофию, хотя это зависит от сложности скрипта, если можно измерить разницу во времени. - person martinstoeckli; 09.04.2012
comment
@martinstoeckli я сделаю код, который попытается объяснить идею. Но на самом деле ограничения проекта делают цель безопасности неоптимальной. - person GuruJR; 10.04.2012
comment
Использование временной метки не является недостатком реализации, если оно используется только как возможная дополнительная энтропия, а не как один из источников обеспечения минимально необходимой энтропии. - person H M; 23.04.2012
comment
@HM ваша сериализация пользовательского ввода ваша база данных - бесплатный обед. - person GuruJR; 18.05.2012

rn_rand() должен привыкнуть, а не rand()

person user3240019    schedule 27.01.2014
comment
Пожалуйста, уточните, почему это решение? - person Patrick Hofman; 27.01.2014
comment
Улучшите свой ответ, пожалуйста. - person Rahil Wazir; 27.01.2014