Как хэш длинные пароли (> 72 символа) с blowfish

На прошлой неделе я прочитал много статей о хэшировании паролей, и Blowfish кажется (одним из) лучшим алгоритмом хэширования прямо сейчас, но это не тема этого вопроса!

Предел 72 символов

Blowfish учитывает только первые 72 символа введенного пароля:

<?php $password = "Wow. This is a super secret and super, super long password. Let's add some special ch4r4ct3rs a#d everything is fine :)"; $hash = password_hash($password, PASSWORD_BCRYPT); var_dump($password); $input = substr($password, 0, 72); var_dump($input); var_dump(password_verify($input, $hash)); ?> 

Выход:

 string(119) "Wow. This is a super secret and super, super long password. Let's add some special ch4r4ct3rs a#d everything is fine :)" string(72) "Wow. This is a super secret and super, super long password. Let's add so" bool(true) 

Как вы можете видеть только первые 72 символа. Twitter использует blowfish aka bcrypt для хранения своих паролей ( https://shouldichangemypassword.com/twitter-hacked.php ), и угадайте, что: измените свой пароль Twitter на длинный пароль с более чем 72 символами, и вы можете войти в свою учетную запись вводя только первые 72 символа.

Blowfish и Pepper

Есть много разных мнений о «переполненных» паролях. Некоторые люди говорят, что это не нужно, потому что вы должны предположить, что секретная перечная строка также известна / опубликована, поэтому она не улучшает хэш. У меня есть отдельный сервер базы данных, поэтому вполне возможно, что просочилась только база данных, а не постоянный перец.

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

Поэтому, я думаю, что перебор пароля – это, по крайней мере, плохой выбор.

Предложение

Мое предложение получить хеш Blowfish для пароля с более чем 72 символами (и перцем):

 <?php $pepper = "foIwUVmkKGrGucNJMOkxkvcQ79iPNzP5OKlbIdGPCMTjJcDYnR"; // Generate Hash $password = "Wow. This is a super secret and super, super long password. Let's add some special ch4r4ct3rs a#d everything is fine :)"; $password_peppered = hash_hmac('sha256', $password, $pepper); $hash = password_hash($password_peppered, PASSWORD_BCRYPT); // Check $input = substr($password, 0, 72); $input_peppered = hash_hmac('sha256', $input, $pepper); var_dump(password_verify($input_peppered, $hash)); ?> 

Это основано на этом вопросе : password_verify return false .

Вопрос

Что является более безопасным способом? Сначала получить хэш SHA-256 (который возвращает 64 символа) или рассмотреть только первые 72 символа пароля?

Pros

  • Пользователь не может войти, введя только первые 72 символа
  • Вы можете добавить перец, не превышающий лимит символов
  • Выход hash_hmac, вероятно, будет иметь больше энтропии, чем сам пароль
  • Пароль хэшируется двумя различными функциями

Cons

  • Только 64 символа используются для создания хэша blowfish

Изменить 1: этот вопрос касается только интеграции PHP blowfish / bcrypt. Спасибо за комментарии!

Проблема здесь в основном связана с проблемой энтропии. Итак, давайте начнем искать там:

Энтропия на персонаж

Количество бит энтропии на каждый байт:

  • Шестигранные символы
    • Биты: 4
    • Значения: 16
    • Энтропия В 72 символах: 288 бит
  • Алфавитно-цифровой
    • Биты: 6
    • Значения: 62
    • Энтропия в 72 символах: 432 бит
  • «Общие» символы
    • Биты: 6.5
    • Значения: 94
    • Энтропия в 72 символах: 468 бит
  • Полный байт
    • Биты: 8
    • Значения: 255
    • Энтропия в 72 символах: 576 бит

Итак, как мы действуем, зависит от того, какой тип символов мы ожидаем.

Первая проблема

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

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

Вторая проблема

Однако sha256 обеспечивает только 256 бит энтропии. Таким образом, вы эффективно разрезаете возможные 576 бит до 256 бит. Ваш хэш-шаг * немедленно *, по самому определению, теряет по меньшей мере 50% возможной энтропии в пароле.

Вы можете частично решить эту проблему, переключившись на SHA512 , где вы уменьшите доступную энтропию примерно на 12%. Но это все еще незначительная разница. Это 12% уменьшает количество перестановок в 1.8e19 . Это большое число … И это тот фактор, который он уменьшает …

Основная проблема

Основная проблема заключается в том, что существует три типа паролей более 72 символов. Влияние, которое эта система стиля оказывает на них, будет сильно отличаться:

Примечание: отсюда я предполагаю, что мы сравниваем с перечной системой, которая использует SHA512 с исходным результатом (не шестнадцатеричным).

  • Случайные пароли с высокой энтропией

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

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

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

  • Случайные пароли средней энтропии

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

    Для этой группы вы немного разблокируете больше энтропии (не создавайте ее, но позволяете больше энтропии вписываться в пароль bcrypt). Когда я говорю немного, я имею в виду немного. Разрыв происходит, когда вы превысите 512 бит, которые имеет SHA512. Таким образом, пик составляет 78 символов.

    Позвольте мне сказать это снова. Для этого класса паролей вы можете сохранить только 6 символов, прежде чем закончите энтропию.

  • Низкие энтропийные неслучайные пароли

    Это группа, которая использует буквенно-цифровые символы, которые, вероятно, не генерируются случайным образом. Что-то вроде цитаты из Библии или таковой. Эти фразы имеют приблизительно 2,3 бита энтропии на символ.

    Для этой группы вы можете значительно разблокировать больше энтропии (не создавать ее, а разрешать больше входить в ввод пароля bcrypt) путем хеширования. Брелок составляет около 223 символов, прежде чем закончится энтропия.

    Скажем так. Для этого класса паролей предварительное хеширование значительно повышает безопасность.

Назад в реальный мир

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

Хотя мало исследований, которые ушли в угадывание энтропии, есть некоторые моменты, которые я хотел бы отметить.

Шансы случайного угадывания 72 правильных символов подряд чрезвычайно низки. Вы, скорее всего, выиграете лотерею Powerball 21 раз, чем это столкновение … Вот как много из числа, о котором мы говорим.

Но мы не можем наткнуться на него статистически. В случае фраз вероятность того, что первые 72 персонажа будут одинаковыми, намного выше, чем для случайного пароля. Но он все еще тривиально низкий (вы, скорее всего, выиграете лотерею Powerball 5 раз, на основе 2,3 бит на символ).

Практически

Практически это не имеет большого значения. Шансы на то, что кто-то угадывает первые 72 персонажа вправо, где последние имеют существенную разницу, настолько малы, что это не стоит беспокоиться. Зачем?

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

Давайте возьмем пример. Давайте возьмем цитату из библии (просто потому, что это общий источник длинного текста, а не по какой-либо другой причине):

Не желай дома своего соседа. Вы не должны жаждать жены своего соседа, его слугу или служанку, его быка или осла или все, что принадлежит вашему соседу.

Это 180 символов. 73-й символ – это g в neighbor's . Если вы так много догадались, вы, вероятно, не остановитесь на ней, но продолжаете с остальными стихами (так как этот пароль, вероятно, будет использоваться). Поэтому ваш «хеш» не добавил многого.

BTW: Я абсолютно НЕ сторонник использования цитаты из Библии. На самом деле, полная противоположность.

Вывод

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

Но, в конце концов, ничто из этого не является чрезмерно значительным. Цифры, с которыми мы имеем дело, просто слишком велики. Разница в энтропии не будет большой.

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

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

Это мои $ 0,02 по крайней мере (или, возможно, более $ 0,02) …

Как далеко, как использовать «секретный» перец:

В буквальном смысле нет исследований по подаче одной хэш-функции в bcrypt. Поэтому в лучшем случае неясно, кормит ли «хэшированный» хэш в bcrypt, когда-либо создаст неизвестные уязвимости (мы знаем, что hash1(hash2($value)) может выявить значительные уязвимости в отношении устойчивости к столкновению и атак с прообразом).

Учитывая, что вы уже планируете хранить секретный ключ («перец»), почему бы не использовать его так, чтобы он хорошо изучался и понимался? Почему бы не зашифровать хеш до его хранения?

В принципе, после того, как вы сделали хэш-пароль, подайте весь хэш-вывод в сильный алгоритм шифрования. Затем сохраните зашифрованный результат.

Теперь атака SQL-Injection не будет утечка ничего полезного, потому что у них нет ключа шифрования. И если ключ просочился, злоумышленники не лучше, чем если бы вы использовали простой хэш (что доказуемо, что-то с перцем «pre-hash» не предоставляет).

Примечание. Если вы решите это сделать, используйте библиотеку. Для PHP я настоятельно рекомендую Zend\Crypt пакет Zend Framework 2. На самом деле это единственный, который я бы рекомендовал в этот момент. Он был сильно пересмотрен, и он принимает все решения для вас (что очень хорошо) …

Что-то вроде:

 use Zend\Crypt\BlockCipher; public function createHash($password) { $hash = password_hash($password, PASSWORD_BCRYPT, ["cost"=>$this->cost]); $blockCipher = BlockCipher::factory('mcrypt', array('algo' => 'aes')); $blockCipher->setKey($this->key); return $blockCipher->encrypt($hash); } public function verifyHash($password, $hash) { $blockCipher = BlockCipher::factory('mcrypt', array('algo' => 'aes')); $blockCipher->setKey($this->key); $hash = $blockCipher->decrypt($hash); return password_verify($password, $hash); } 

И это полезно, потому что вы используете все алгоритмы способами, которые хорошо поняты и хорошо изучены (относительно по крайней мере). Запомнить:

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

  • Брюс Шнайер

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

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

Тогда что действительно помогает перцу? Пока перец остается в секрете, он защищает слабые пароли от атаки словаря. Пароль 1234 затем станет чем-то вроде 1234-p*deDIUZeRweretWy+.O . Этот пароль не только намного длиннее, но и содержит специальные символы и никогда не будет частью какого-либо словаря.

Теперь мы можем оценить, какие пароли будут использовать наши пользователи, возможно, больше пользователей будут вводить слабые пароли, так как есть пользователи с паролями между 64-72 символами (на самом деле это будет очень редко).

Еще один момент – это диапазон для принудительного принуждения. Хэш-функция sha256 будет возвращать 256-битные выходы или комбинации 1.2E77, это слишком много для принудительного принуждения, даже для графических процессоров (если бы я правильно вычислил, это потребует около 2E61 лет на графическом процессоре в 2013 году). Таким образом, мы не получаем настоящего недостатка, применяя перец. Поскольку значения хэша не являются систематическими, вы не можете ускорить грубое форсирование с помощью общих шаблонов.

PS Насколько я знаю, ограничение 72 символов специфично для алгоритма самого BCrypt. Лучший ответ, который я нашел, – это .

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

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

Рекомендуемый префикс пароля 56 байтов (включая пустой байт завершения) для bcrypt относится к пределу 448 бит ключа Blowfish. Любые байты, превышающие этот предел, не полностью смешиваются с полученным хешем. Таким образом, абсолютный предел в байтах с байтом в 72 байта является менее актуальным, если вы рассматриваете фактический эффект на полученный хэш этими байтами.

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

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

Если вы решили предварительно ввести пароль, длина которого превышает 55 байт, тогда вы должны использовать SHA-384, так как он имеет наибольший выход без превышения лимита.