Это хорошая функция хеширования пароля в PHP? Если нет, то почему нет?

Мне интересно, достаточно ли хороша эта функция (которая частично взята из ~2-летней версии phpBB).

Если нет, то почему?
И как бы вы изменили это (сделав переход бесшовным для существующих пользователей)?

Результатом hash_pwd() будет то, что будет сохранено в БД.

function hash_pwd($password)
{
    $itoa64 = './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';

    $random_state = $this->unique_id();
    $random = '';
    $count = 6;

    if (($fh = @fopen('/dev/urandom', 'rb')))
    {
        $random = fread($fh, $count);
        fclose($fh);
    }

    if (strlen($random) < $count)
    {
        $random = '';

        for ($i = 0; $i < $count; $i += 16)
        {
            $random_state = md5($this->unique_id() . $random_state);
            $random .= pack('H*', md5($random_state));
        }
        $random = substr($random, 0, $count);
    }

    $hash = $this->_hash_crypt_private($password, $this->_hash_gensalt_private($random, $itoa64), $itoa64);

    if (strlen($hash) == 34)
    {
        return $hash;
    }

    return false;
}


function unique_id()
{
    $val = microtime();
    $val = md5($val);

    return substr($val, 4, 16);
}

function _hash_crypt_private($password, $setting, &$itoa64)
{
    $output = '*';

    // Check for correct hash
    if (substr($setting, 0, 3) != '$H$')
    {
        return $output;
    }

    $count_log2 = strpos($itoa64, $setting[3]);

    if ($count_log2 < 7 || $count_log2 > 30)
    {
        return $output;
    }

    $count = 1 << $count_log2;
    $salt = substr($setting, 4, 8);

    if (strlen($salt) != 8)
    {
        return $output;
    }

    /**
    * We're kind of forced to use MD5 here since it's the only
    * cryptographic primitive available in all versions of PHP
    * currently in use.  To implement our own low-level crypto
    * in PHP would result in much worse performance and
    * consequently in lower iteration counts and hashes that are
    * quicker to crack (by non-PHP code).
    */
    if (PHP_VERSION >= 5)
    {
        $hash = md5($salt . $password, true);
        do
        {
            $hash = md5($hash . $password, true);
        }
        while (--$count);
    }
    else
    {
        $hash = pack('H*', md5($salt . $password));
        do
        {
            $hash = pack('H*', md5($hash . $password));
        }
        while (--$count);
    }

    $output = substr($setting, 0, 12);
    $output .= $this->_hash_encode64($hash, 16, $itoa64);

    return $output;
}

function _hash_gensalt_private($input, &$itoa64, $iteration_count_log2 = 6)
{
    if ($iteration_count_log2 < 4 || $iteration_count_log2 > 31)
    {
        $iteration_count_log2 = 8;
    }

    $output = '$H$';
    $output .= $itoa64[min($iteration_count_log2 + ((PHP_VERSION >= 5) ? 5 : 3), 30)];
    $output .= $this->_hash_encode64($input, 6, $itoa64);

    return $output;
}

function _hash_encode64($input, $count, &$itoa64)
{
    $output = '';
    $i = 0;

    do
    {
        $value = ord($input[$i++]);
        $output .= $itoa64[$value & 0x3f];

        if ($i < $count)
        {
            $value |= ord($input[$i]) << 8;
        }

        $output .= $itoa64[($value >> 6) & 0x3f];

        if ($i++ >= $count)
        {
            break;
        }

        if ($i < $count)
        {
            $value |= ord($input[$i]) << 16;
        }

        $output .= $itoa64[($value >> 12) & 0x3f];

        if ($i++ >= $count)
        {
            break;
        }

        $output .= $itoa64[($value >> 18) & 0x3f];
    }
    while ($i < $count);

    return $output;
}

4 ответа

Решение

Код, который вы дали, является портом PHPASS, в частности, "переносимым" алгоритмом. Обратите внимание на квалификацию portable, Это относится только к phpass библиотека, если вы передадите true в качестве второго параметра конструктора. С этого момента в этом ответе, phpass относится ТОЛЬКО к переносимому алгоритму, а не к самой библиотеке. Библиотека будет делать bcrypt по умолчанию, если вы не укажете явно portable...

Команда PHPBB не развивает себя (очень хорошо), но ее портировали из phpass непосредственно (спорное).

Есть несколько вопросов, которые мы должны задать здесь:

Это плохо?

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

Каковы некоторые недостатки?

  • Относительно pbkdf2: phpass алгоритм использует hash() где pbkdf2() использования hash_hmac(), Теперь HMAC запускает 2 хеша для каждого внутреннего вызова, но реализация PHP занимает примерно в 1,6 раза больше, чем один вызов hash() (разве С не замечательный?). Таким образом, мы получаем 2 хэша от hash_hmac в 62% случаев это займет hash() выполнить 2 хэша.

    Что это значит? Ну, для данного времени выполнения, pbkdf2 будет работать примерно на 37,5% больше хэшей, чем phpass алгоритм. Больше хэшей за данное время == хорошо, потому что это приводит к большему количеству вычислений.

    Так pbkdf2 примерно 37.5% сильнее чем phpass при использовании одного и того же примитива (md5 в этом случае). Но pbkdf2 Можно также взять более сильные примитивы. Таким образом, мы можем использовать pbkdf2 с sha512 чтобы получить очень значительное преимущество над phpass алгоритм (в основном потому, что sha512 сложный алгоритм с большим количеством вычислений, чем md5).

    Это означает, что не только pbkdf2 способен генерировать больше вычислений за одно и то же время, он может генерировать более сложные вычисления.

    С учетом сказанного, разница не слишком значительна. Это очень измеримо, и pbkdf2 определенно "сильнее", чем phpass,

  • Относительно bcrypt: Сравнение сделать намного сложнее. Но давайте посмотрим на поверхность этого. phpass использования md5 и цикл в PHP. pbkdf2 использует любой примитив (в C) и цикл в PHP. bcrypt использует собственный алгоритм на языке C (это означает, что он отличается от любого доступного хэша). Так что право летучей мыши, bcrypt имеет существенное преимущество, заключающееся в том, что алгоритм все в C. Это позволяет больше "вычислений" в единицу времени. Тем самым делая его более эффективным медленным алгоритмом (больше вычислений в заданной среде выполнения).

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

    Один из примеров более сильной природы bcrypt тот факт, что bcrypt использует гораздо большее внутреннее состояние, чем обычная хеш-функция. SHA512 использует 512-битное внутреннее состояние для вычисления блока из 1024 бит. bcrypt вместо этого использует около 32 КБ внутреннего состояния для вычисления одного блока из 576 бит. Дело в том, что bcrypt внутреннее состояние намного больше, чем SHA512 (а также md5 а также phpass частично объясняет более сильную природу bcrypt,

Следует ли этого избегать

Для новых проектов, абсолютно. Не то чтобы это плохо. Это не так. Дело в том, что существуют явно более сильные алгоритмы (на порядок). Так почему бы не использовать их?

Для дальнейшего доказательства того, как bcrypt сильнее, посмотрите Slides from Password13 (PDF), который запустил кластер 25 GPU для взлома хэшей паролей. Вот соответствующие результаты:

  • md5($password)
    • 180 миллиардов угаданий в секунду
    • 9,4 часа - все возможные 8-символьные пароли
  • sha1($password)
    • 61 млрд угаданий в секунду
    • 27 часов - все возможные 8-символьные пароли
  • md5crypt (что очень похоже на phpass со стоимостью 10):
    • 77 миллионов догадок в секунду
    • 2,5 года - все возможные 8-символьные пароли
  • bcrypt со стоимостью 5
    • 71 тысяча догадок в секунду
    • 2700 лет - все возможные 8-символьные пароли

Примечание: все возможные 8-символьные пароли используют набор из 94 символов:

a-zA-Z0-9~`!@#$%^&*()_+-={}|[]\:";'<>,.?/

Суть

Так что, если вы пишете новый код, без сомнения, используйте bcrypt, Если у вас есть phpass или же pbkdf2 сейчас вы можете обновиться, но это не совсем ясно: "вы значительно уязвимы".

Быстрый ответ:

Используйте библиотеку bcrypt (когда она будет готова) или password_compat из ircmaxell - это библиотека bcrypt.

Длинный ответ:

Это слишком сложно и устарело для современных технологий. Md5 следует избегать, и просто засолки недостаточно. Используйте bcrypt и избавьте себя от головной боли.

Вы можете спросить себя, почему эта конкретная библиотека? Ну и те же функции будут доступны в php 5.5, поэтому вам не придется менять кодировку. Желаем удачи и делайте это простым и эффективным. Также медленно это хорошо для входа и паролей.

Обновление 1

@ Gumbo -> Нет MD5 не сломан, но основное назначение хэшей, таких как MD5 сейчас и в прошлом, было для проверки файлов (как вы знаете, чтобы проверить, можно ли доверять содержимому файла, а не хранения паролей), поскольку хэши очень быстро расшифровать, так как вы не будете использовать Bcrypt для проверки содержимого файла, так как вы ждете 30 - 45 секунд... Так что это означает, что хеш специально предназначен для быстрого чтения. Даже SHA512 по сравнению с bcrypt по-прежнему полностью уступает. Вот почему настоятельно необходимо использовать надежные алгоритмы паролей, такие как Blowfish/Bcrypt в PHP. Мы, как пользователи, так и программисты, должны расширить знания о том, что простое хранение паролей или алгоритмы хэширования низкого уровня НЕ являются ответом и никогда не должны использоваться для такой важной информации.

Теперь операционному оператору для перехода к новой системе вы должны сначала отправить всем пользователям уведомление о том, что "В целях безопасности обновлена ​​система шифрования паролей........", а затем спросить их. для одноразового обновления пароля, когда вы выполните обновление пароля, вы будете использовать функцию password_verify, и если вы хотите получить максимальный контроль над соотношением затрат, вы используете password_needs_rehash, если по какой-то причине вы решите изменить стоимость, связанную с паролями,

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

Надеемся, что это отвечает на большинство вопросов, но ответ IRCmaxell намного лучше, если вдаваться в детали по различным алгоритмам! Удачи, OP, и большое спасибо ircmaxell!

Обновление 2

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

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

Bcrypt CURRENTLY - лучший способ хранения паролей! Однако, если вы посмотрите на Scrypt, это кажется многообещающим, но не поддерживается PHP. Тем не менее, любая вещь и все сломано, это просто вопрос времени, пока какой-нибудь "выродок" в подвале взломает Bcrypt. Но сейчас мы в безопасности... точно так же, как мы в безопасности с IPv4, который никогда не заканчивается.... о, подождите?...;)

Надеюсь, это ответит на ваш вопрос, который вы подняли, сэр. Кроме того, просто чтобы выразить это в контексте, моя система стоит 17, и для входа в нее требуется ~20 - ~30 секунд, но когда я представил систему своему профессору и моим клиентам, и почему она должна быть обязательной, они полюбили ее и чувствовал себя уверенным, что они были защищены.

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

Реализация: как вы используете bcrypt для хеширования паролей в PHP?

(Извините за мой английский вначале)

Есть несколько вопросов, которые все комментаторы должны задать, прежде чем ответить:

Где будет использоваться этот код?

Какие данные он будет защищать?

Будет ли он использоваться для системы критического уровня?

Хорошо, у нас есть несколько вариантов использования. Я использую упрощенный генератор паролей для нашей IS, да, на самом деле это не "готовность к 3-й мировой войне", но вероятность того, что пользователь сообщит кому-то пароль, или он будет просочиться из-за какого-либо вредоносного ПО со своего компьютера, все еще намного больше.

  • MD5 слаб, используйте новейший генератор отпечатков пальцев (sha128,sha256,sha512)
  • используйте соль произвольной длины, например:

    private function generateSalt() {
      $salt = '';
      $i = rand(20, 40); //min,max length
      for($length = 0; $length < $i; $length++) {
        $salt .= chr(rand(33, 126));
      }        
      return $salt;
    }
    
  • затем сгенерируйте пароль:

    private function generatePass() {
      $vocabulary = 'abcdefghijklmnopqrstuvwxyz0123456789';
      $password = '';
      $i = rand(7,10);
      for($length = 0; $length < $i; $length++) {
          $password .= substr($vocabulary,rand(0, 35),1);
      }        
      return $password.'-'; // avoid copy`n`paste :)
    }
    
  • да пароль должен быть надежнее, но пользователь никогда его не запомнит, он будет сохранен в браузере или где-то записан. Ожидания и безопасность против реальности.

    $newSalt = $this->generateSalt();
    $newPass = $this->generatePass();
    $newHash = hash('sha512', $newPass.':'.$newSalt); 
    // now store hash and salt into database
    
  • Есть новый хэш, но он еще не закончен, настоящая работа началась:

    1. протоколирование
    2. защита сессии
    3. user,user+ip,ip temp/perm baning
    4. так далее

вход в систему + выход из системы + повторное создание / создание пароля: 1 или 2 таблицы SQL и несколько килобайт кода

другие вещи о безопасности: много таблиц, действительно больше, чем несколько килобайт кода

Другие вопросы по тегам