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

Безопасное генерирование случайных чисел в PHP

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

Решение, которое я придумал до сих пор, включает в себя сохранение начального семени, затем для каждого вызова

result = seed
seed = sha512(seed . mt_rand())

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

Я что-то упустил или есть более известные решения?

4b9b3361

Ответ 1

Вы также можете использовать OpenSSL openssl_random_pseudo_bytes, доступный с PHP 5.3.

 string openssl_random_pseudo_bytes ( int $length [, bool &$crypto_strong ] )

Генерирует строку псевдослучайных байтов с количеством байтов, определяемым параметром длины. Он также указывает, использовался ли криптографически сильный алгоритм для создания псевдослучайных байтов и делает это с помощью необязательного параметра crypto_strong. Для этого редко бывает ЛОЖЬ, но некоторые системы могут быть разбиты или стары.

http://www.php.net/manual/en/function.openssl-random-pseudo-bytes.php

Так как PHP 7 существует также функция random_bytes

string random_bytes ( int $length )

http://php.net/manual/en/function.random-bytes.php

Ответ 2

Я настоятельно рекомендую использовать таргетинг /dev/urandom в системах unix или криптоапи на платформе Windows в качестве источника энтропии для паролей.

Я не могу подчеркнуть, что важность реализации хэшей - это не волшебные энтропийные устройства. Неправильное использование их таким образом не более безопасно, чем использование данных seed и rand() до того, как оно было хэшировано, и я уверен, что вы признаете, что это не очень хорошая идея. Семя отменяет (детерминированный mt_rand()), и поэтому нет никакого смысла даже в том, чтобы включить его.

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

Две ошибки не делают правильного. Система настолько же сильна, как и ее самая слабая часть. Это не лицензия или оправдание, чтобы признать, что еще больше это небезопасно.


Вот некоторый код PHP для получения безопасной случайной 128-битной строки, от этот комментарий на php.net от Mark Seecof:

"Если вам нужны псевдослучайные биты для безопасности или криптографические цели (яйцо, случайный IV для блочного шифрования, случайная соль для хэша пароля) mt_rand() - плохой источник. На большинстве платформ Unix/Linux и/или MS-Windows вы можете получить более качественную оценку псевдослучайных битов из ОС или системной библиотеки, например:

<?php
// get 128 pseudorandom bits in a string of 16 bytes

$pr_bits = '';

// Unix/Linux platform?
$fp = @fopen('/dev/urandom','rb');
if ($fp !== FALSE) {
    $pr_bits .= @fread($fp,16);
    @fclose($fp);
}

// MS-Windows platform?
if (@class_exists('COM')) {
    // http://msdn.microsoft.com/en-us/library/aa388176(VS.85).aspx
    try {
        $CAPI_Util = new COM('CAPICOM.Utilities.1');
        $pr_bits .= $CAPI_Util->GetRandom(16,0);

        // if we ask for binary data PHP munges it, so we
        // request base64 return value.  We squeeze out the
        // redundancy and useless ==CRLF by hashing...
        if ($pr_bits) { $pr_bits = md5($pr_bits,TRUE); }
    } catch (Exception $ex) {
        // echo 'Exception: ' . $ex->getMessage();
    }
}

if (strlen($pr_bits) < 16) {
    // do something to warn system owner that
    // pseudorandom generator is missing
}
?>

NB: в целом безопасно оставлять как попытку чтения /dev/urandom, так и попытку доступа к CAPICOM в вашем коде, хотя каждый из них будет терпеть неудачу на другой платформе. Оставьте их обоих, чтобы ваш код был более портативным.

Ответ 3

PHP поставляется с новым набором функций CSPRNG (random_bytes() и random_int()). Тривиально превратить последнюю функцию в функцию генератора строк:

<?php
/**
 * Generate a random string, using a cryptographically secure 
 * pseudorandom number generator (random_int)
 * 
 * For PHP 7, random_int is a PHP core function
 * For PHP 5.x, depends on https://github.com/paragonie/random_compat
 * 
 * @param int $length      How many characters do we want?
 * @param string $keyspace A string of all possible characters
 *                         to select from
 * @return string
 */
function random_str(
    $length,
    $keyspace = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
) {
    $str = '';
    $max = mb_strlen($keyspace, '8bit') - 1;
    if ($max < 1) {
        throw new Exception('$keyspace must be at least two characters long');
    }
    for ($i = 0; $i < $length; ++$i) {
        $str .= $keyspace[random_int(0, $max)];
    }
    return $str;
}

Если вам нужно использовать это в проекте PHP 5, не стесняйтесь брать копию random_compat, которая является polyfill для этих функций.