Как хешировать длинные пароли (›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, также известный как bcrypt, для хранения своих паролей (https://shouldichangemypassword.com/twitter-hacked.php) и угадайте, что: измените свой пароль Twitter на длинный пароль, содержащий более 72 символов, и вы сможете войти в свою учетную запись, введя только первые 72 символа.

Blowfish и перец

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

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

Так что я думаю, что добавление пароля - по крайней мере, неплохой выбор.

Предположение

Мое предложение получить хэш 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 символа пароля?

Плюсы

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

Минусы

  • Для построения хэша blowfish используются только 64 символа.


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


person Frederik Kammer    schedule 16.05.2013    source источник
comment
Blowfish - не единственный, кто урезает пароль, заставляя людей думать, что это безопаснее, чем есть на самом деле. Вот интересная история 8- ограничение на количество символов.   -  person DOK    schedule 16.05.2013
comment
Является ли 72-символьное усечение фундаментальным для алгоритма Blowfish или только для реализации PHP? IIRC Blowfish также используется (по крайней мере, в некоторых) для шифрования паролей пользователей.   -  person Douglas B. Staple    schedule 16.05.2013
comment
Проблема связана с Bcrypt, а не с Blowfish. Я могу воспроизвести эту проблему только с помощью Python и Bcrypt.   -  person Blender    schedule 16.05.2013
comment
@Blender: Спасибо за ваш комментарий и вашу работу над ним. Мне не удалось найти в php разные функции для blowfish и bcrypt, хотя они одинаковы. Но разве для меня это не имеет значения на php? Я бы предпочел использовать стандартную функцию php.   -  person Frederik Kammer    schedule 17.05.2013
comment
Дуглас, да, ограничение в 72 символа является фундаментальным для BCrypt. Вы можете расширить его (например, расширенный шифрование DES по сравнению со стандартным DES), но это будет нестандартно.   -  person Zer    schedule 17.05.2013
comment
Также см. структуру хеширования паролей PHP (PHPass) от Openwall. Он портативен и защищен от ряда распространенных атак на пароли пользователей. Фреймворк (SolarDesigner) написал тот же парень, который написал John The Ripper и сидит как судья в конкурсе по хешированию паролей. Так что он кое-что знает об атаках на пароли.   -  person jww    schedule 12.10.2014
comment
Bcrypt использует алгоритм Blowfish для хеширования паролей. Поэтому вы не можете использовать bcrypt отдельно от Blowfish.   -  person Phil    schedule 13.02.2017


Ответы (3)


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

Энтропия на символ

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

  • Hex Characters
    • Bits: 4
    • Значения: 16
    • Энтропия в 72 символах: 288 бит
  • Alpha-Numeric
    • Bits: 6
    • Значения: 62
    • Энтропия в 72 символах: 432 бита
  • "Common" Symbols
    • Bits: 6.5
    • Значения: 94
    • Энтропия в 72 символах: 468 бит
  • Full Bytes
    • Bits: 8
    • Значения: 255
    • Энтропия в 72 символах: 576 бит

Итак, то, как мы будем действовать, зависит от того, каких персонажей мы ожидаем.

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

Первая проблема с вашим кодом заключается в том, что на шаге хеширования "перец" выводятся шестнадцатеричные символы (поскольку четвертый параметр 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. Если вы так много догадались, вы, вероятно, не остановитесь на nei, а продолжите читать остальную часть стиха (поскольку именно так, вероятно, будет использоваться пароль). Таким образом, ваш "хеш" ничего не добавил.

BTW: Я АБСОЛЮТНО НЕ защищаю использование цитаты из Библии. На самом деле с точностью до наоборот.

Вывод

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

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

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

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

По крайней мере, это мои 0,02 доллара (или, возможно, больше, чем 0,02 доллара) ...

Что касается использования "секретного" перца:

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

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

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

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

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

Что-то типа:

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);
}

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

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

person ircmaxell    schedule 16.05.2013
comment
Большое спасибо за подробный ответ. Это мне очень помогает! - person Frederik Kammer; 17.05.2013
comment
Мой комплимент за этот ответ. Одна маленькая придирка: это подавляющее большинство пользователей, которые используют очень слабые пароли, слова и производные, содержащиеся в словаре, для взлома паролей, перец защитит их независимо от вопросов энтрофии. Чтобы не потерять энтрофию, вы можете просто объединить пароль и перец. Однако ваше предложение о шифровании хеш-значения, вероятно, является лучшим решением для добавления секрета на стороне сервера. - person martinstoeckli; 17.05.2013
comment
@martinstoeckli: Моя проблема с концепцией перца не в его ценности. Дело в том, что применение перца выходит на неизведанную территорию с точки зрения криптографических алгоритмов. Это нехорошо. Вместо этого я считаю, что криптографические примитивы следует комбинировать таким образом, чтобы они были созданы для совместной работы. По сути, основная концепция перца звучит для меня в ушах, как некоторые люди, ничего не знающие о криптографии, сказали: Чем больше хешей, тем лучше! У нас есть соль, перец тоже хорош!. Я просто предпочел бы иметь более простой, более проверенный и более простой импл - person ircmaxell; 17.05.2013
comment
@ircmaxell - Да, я знаю вашу точку зрения и согласен, если после этого хеш-значения будут зашифрованы. Если вы не сделаете этот дополнительный шаг, атака по словарю просто обнаружит слишком много слабых паролей даже при наличии хорошего алгоритма хеширования. - person martinstoeckli; 17.05.2013
comment
@martinstoeckli: Я с этим не согласен. Хранение секретов - дело нетривиальное. Вместо этого, если вы используете bcrypt с хорошей стоимостью (12 сегодня), все пароли, кроме самого слабого, будут безопасными (словарь и простые пароли являются слабыми). Поэтому я бы рекомендовал людям сосредоточиться на обучении пользователя измерители силы и, в первую очередь, заставить их использовать более надежные пароли ... - person ircmaxell; 17.05.2013
comment
@ircmaxell - самый слабый класс паролей - не самый редкий. Чтобы определить, будет ли пароль взломан при реальной атаке по словарю, измеритель силы потребует отдельного словаря. Утечка базы данных представляет собой реальную угрозу (SQL-инъекция, выброшенные резервные копии, отброшенные серверы ...), поэтому добавление секрета на стороне сервера не является академическим шагом, это помогает, даже если ключ хранится где-то в жестком коде. Ваше решение с шифрованием хеш-значения будет иметь еще одно преимущество, оно позволяет при необходимости обменивать ключ, мне нравится эта идея. - person martinstoeckli; 17.05.2013
comment
Я думаю, вы должны учитывать возможность того, что кому-то может понадобиться более 72 символов. Что, если они будут использовать содержимое как файл для ключа (grandma.jpg), и будет использоваться только заголовок файла? Что, если чей-то пароль будет скомпрометирован, и он изменит только последнюю его часть? Было бы лучше выдать ошибку, если вы не хотите поддерживать длинные пароли, чем делать такие тихие предположения. - person Daan Bakker; 11.10.2015

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

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

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

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

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

P.S. Насколько мне известно, ограничение в 72 символа зависит от самого алгоритма BCrypt. Лучший ответ, который я нашел, - это.

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

person martinstoeckli    schedule 16.05.2013
comment
Что касается вашего P.P.S, я могу просто сказать: да, он может проверить усеченный пароль с хешем неусеченного и все равно получить true. Вот о чем весь этот вопрос. Посмотрите сами: viper-7.com/RLKFnB - person Sliq; 01.06.2013
comment
@Panique - Проблема не в вычислении хэша BCrypt, а в HMAC раньше. Для генерации хэша SHA OP использует полный пароль и использует результат в качестве входных данных для BCrypt. Для проверки он усекает пароль перед вычислением хэша SHA, а затем использует этот совершенно другой результат в качестве входных данных для BCrypt. HMAC принимает ввод любой длины. - person martinstoeckli; 01.06.2013

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

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

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

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

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

person Phil    schedule 13.02.2017
comment
Срок действия пароля также должен быть коротким, например, две недели очень длинных паролей, правда, зачем вообще сохранять пароль, просто используйте сброс пароля каждый раз. Серьезно, это неправильное решение, переходить на двухфакторную аутентификацию с помощью токена. - person zaph; 13.02.2017
comment
Спасибо @zaph. Можете ли вы указать мне на пример этого? Звучит интересно. - person Phil; 22.02.2017
comment
[ПРОЕКТ Специальной публикации NIST 800-63B Рекомендации по цифровой аутентификации] (pages.nist .gov / 800-63-3 / sp800-63b.html), 5.1.1.2. Запомненные верификаторы секретов: Верификаторы НЕ ДОЛЖНЫ требовать произвольного изменения запомненных секретов (например, периодически). См. Также раздел «На пути к лучшим требованиям к паролю» Джима Фентона. - person zaph; 22.02.2017
comment
Дело в том, что чем чаще пользователю требуется изменить пароль, тем хуже становится выбор пароля, что снижает безопасность. У пользователя ограниченное количество хорошо запоминаемых паролей, и они заканчиваются, либо выбирая действительно плохие пароли, либо записывая их на стикерах, прикрепленных к нижней части клавиатуры, и т. Д. - person zaph; 22.02.2017