Является ли это хорошей функцией пароля хеширования в 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 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;
}

Ответ 1

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

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

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

Неужели это плохо?

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

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

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

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

    So 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 использует вместо 32kb внутреннего состояния для вычисления по одному блоку из 576 бит. Тот факт, что внутреннее состояние bcrypt намного больше, чем sha512md5 и phpass), частично объясняет более сильный характер bcrypt.

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

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

Для дальнейшего доказательства того, как bcrypt сильнее, просмотрите Слайды из 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~`[email protected]#$%^&*()_+-={}|[]\:";'<>,.?/

Нижняя линия

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

Ответ 2

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

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

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

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

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

Обновление 1

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

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

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

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

Обновление 2

     

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

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

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

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

Ответ 3

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

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

Ответ 4

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

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

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

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

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

Хорошо, у нас есть прецедент. Я использую упрощенный генератор паролей для 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
    
  • Появился новый хеш, но он еще не закончен, началась настоящая работа:

    • протоколирование
    • защита сеанса
    • пользователь, пользователь + ip, ip temp/perm banning
    • и т.д.

login + logout + re/generate password: 1 или 2 таблицы SQL и несколько килобайт кода

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