Подтвердить что ты не робот

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

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

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

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

note: pepper - это случайная строка для каждого сайта/установки, заданная вручную.

тогда я выполняю следующий (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())))); 

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

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

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

4b9b3361

Ответ 1

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

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

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

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

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

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

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

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

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


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

Ответ 2

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

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

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 минут?

Ответ 3

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

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

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

$randomBinaryString = mcrypt_create_iv($length, MCRYPT_DEV_URANDOM);

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

Ответ 4

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

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

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

Ответ 5

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

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

Обновление 1. Для реальной защиты вам нужно иметь UN-guessable randomess в вашей энтропии. Подходящий вариант для добавления энтропии имеет ваш вопрос "Ссылка" - использовать Delta вашего script времени выполнения Not microtime() самостоятельно. Потому что Delta полагается на загрузку вашего сервера. Так же сочетается аппаратная среда, температура, сетевая нагрузка, энергонагрузка, доступ к диску, использование процессора и колебания напряжения, которые вместе непредсказуемы.

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

Script выполнение 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

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

Ответ 6

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

Ответ 7

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

phpseclib crypt_random эффективно и тихо возвращается к mt_rand (который должен знать, что он действительно слаб) в худшем случае (нет openssl_random_pseudo_bytes или urandom), но в таких случаях моя функция использует гораздо более безопасную схему. это просто возврат к схеме, которая грубо заставляет/прогнозирует ее выход намного сложнее и должна быть на практике достаточной для всех обычных приложений/сайтов. он использует возможную (на практике очень вероятную и трудно прогнозируемую/обходную) дополнительную энтропию, которая собирается со временем, что быстро становится почти невозможно узнать для посторонних. он добавляет эту возможную энтропию к выходу mt_rand (а также к выходному сигналу других источников: urandom, openssl_random_pseudo_bytes, mcrypt_create_iv). если u проинформирован u должен знать, эта энтропия может быть добавлена, но не вычтена. в (почти наверняка очень редком) худшем случае, что дополнительная энтропия будет равна 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 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() 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 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 содержит мою дополнительную энтропию, которая исходит из всех параметров энтропии запросов, объединенных до сих пор + текущих параметров запроса энтропии + энтропии случайной строки (*), установленной вручную во время установки.

*: length: 22, состоящий из нижних и прописных букв + чисел (более 128 бит энтропии)

Ответ 8

rn_rand() следует использовать не rand()