Сбор энтропии в веб-приложениях для создания (более) безопасных случайных чисел - PullRequest
12 голосов
/ 27 марта 2012

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

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

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

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

, затем я выполняю следующий (Мой) 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.эта система является просто вспомогательной системой или (когда у нас нет (доступа) к этим источникам) резервной схемы.

Ответы [ 8 ]

11 голосов
/ 01 апреля 2012

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

Как они это сделают? Почему, базовое применение теории информации и немного знаний из криптографии , конечно же!

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

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

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

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

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

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

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


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

4 голосов
/ 03 апреля 2012

Использовать Random.Org

Если вам нужны действительно случайные числа, используйте random.org . Эти числа генерируются через атмосферный шум . Помимо библиотеки для PHP , она также имеет 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 вообще работать так, но если Вы действительно обеспокоены тем, что можете уменьшить шанс, обманывая нападающего фиктивным запросом, который вы выбрасываете, или используйте только определенную часть случайных чисел, которые вы получаете.

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

Performance

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

1 голос
/ 07 апреля 2012

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

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

Для чтения из случайного источника вы можете вызвать функцию mcrypt_create_iv() ...

$randomBinaryString = mcrypt_create_iv($length, MCRYPT_DEV_URANDOM);

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

0 голосов
/ 27 января 2014

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

0 голосов
/ 10 апреля 2012

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

phpseclib crypt_random эффективно и бесшумно возвращается к 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:
        // /1193476/opensslrandompseudobytes-medlenno-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;

}

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

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

0 голосов
/ 07 апреля 2012

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

0 голосов
/ 07 апреля 2012

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

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

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

Использование 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

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

0 голосов
/ 06 апреля 2012

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

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

UPDATE: конечно, у вас должна быть резервная копия на случай, если не удается открыть устройство

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