На прошлой неделе я прочитал много статей о хешировании паролей, и на данный момент 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
возврат false
.
Вопрос
Какой способ безопаснее? Сначала получить хеш SHA-256 (который возвращает 64 символа) или учитывать только первые 72 символа пароля?
Плюсы
- Пользователь не может войти в систему, введя только первые 72 символа.
- Вы можете добавить перец, не превышая лимит символов
- Выход hash_hmac, вероятно, будет иметь больше энтропии, чем сам пароль
- Пароль хешируется двумя разными функциями.
Минусы
- Для построения хэша blowfish используются только 64 символа.
Изменить 1: этот вопрос касается только интеграции PHP blowfish / bcrypt. Спасибо за комментарии!
Ответы:
Проблема здесь в основном в проблеме энтропии. Итак, начнем искать там:
Энтропия на символ
Количество бит энтропии на байт:
Итак, то, как мы будем действовать, зависит от того, каких персонажей мы ожидаем.
Первая проблема
Первая проблема с вашим кодом заключается в том, что ваш шаг хеширования "перец" выводит шестнадцатеричные символы (поскольку четвертый параметр
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: Я АБСОЛЮТНО НЕ защищаю использование цитаты из Библии. На самом деле с точностью до наоборот.
Вывод
На самом деле вы не очень сильно поможете людям, использующим длинные пароли, путем хеширования. Некоторым группам вы определенно можете помочь. Некоторым вы определенно можете навредить.
Но, в конце концов, все это не имеет особого значения. Числа мы имеем дело с просто WAY слишком высоко. Разница в энтропии не будет большой.
Вам лучше оставить bcrypt как есть. У вас больше шансов испортить хеширование (буквально, вы это уже сделали, и вы не первый или последний, кто совершит эту ошибку), чем атака, которую вы пытаетесь предотвратить.
Сосредоточьтесь на обеспечении безопасности остальной части сайта. И добавьте измеритель энтропии пароля в поле пароля при регистрации, чтобы указать надежность пароля (и указать, если пароль слишком длинный, чтобы пользователь мог его изменить) ...
По крайней мере, это мои 0,02 доллара (или, возможно, больше, чем 0,02 доллара) ...
Что касается использования «секретного» перца:
Буквально нет исследований по загрузке одной хеш-функции в bcrypt. Следовательно, в лучшем случае неясно, приведет ли когда-либо подача «приправленного» хэша в bcrypt к неизвестным уязвимостям (мы знаем, что это
hash1(hash2($value))
может выявить значительные уязвимости, связанные с сопротивлением столкновениям и атаками с прообразом).Учитывая, что вы уже подумываете о хранении секретного ключа («перца»), почему бы не использовать его хорошо изученным и понятным способом? Почему бы не зашифровать хэш перед его сохранением?
По сути, после того, как вы хешируете пароль, передайте весь хеш-вывод в надежный алгоритм шифрования. Затем сохраните зашифрованный результат.
Теперь атака SQL-инъекции не даст утечки ничего полезного, потому что у них нет ключа шифрования. И если ключ просочился, злоумышленникам ничуть не лучше, чем если бы вы использовали простой хеш (что доказуемо, чего-то с перцем "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 Я думаю, что ваш пример ошибочен, вы не можете сгенерировать хеш с полной длиной пароля и проверить его с помощью усеченного. Вероятно, вы хотели применить перец одинаково для генерации хеша и для проверки хеша.
источник
true
. Вот о чем весь этот вопрос. Посмотрите сами: viper-7.com/RLKFnBBcrypt использует алгоритм, основанный на дорогостоящем алгоритме настройки ключа Blowfish.
Рекомендуемый предел пароля в 56 байтов (включая нулевой байт завершения) для bcrypt относится к пределу в 448 бит ключа Blowfish. Любые байты, превышающие этот предел, не полностью смешиваются в результирующем хэше. Абсолютное ограничение в 72 байта для паролей bcrypt поэтому менее актуально, если учесть фактическое влияние этих байтов на результирующий хэш.
Если вы думаете, что ваши пользователи обычно выбирают пароли длиной более 55 байт, помните, что вы всегда можете вместо этого увеличить количество раундов растяжения пароля, чтобы повысить безопасность в случае нарушения таблицы паролей (хотя это должно быть много по сравнению с добавлением дополнительных символы). Если права доступа пользователей настолько важны, что обычно пользователям требуется очень длинный пароль, то срок действия пароля также должен быть коротким, например, 2 недели. Это означает, что вероятность того, что пароль останется действительным, гораздо ниже, пока хакер вкладывает свои ресурсы в устранение рабочего фактора, задействованного в тестировании каждого пробного пароля, чтобы увидеть, будет ли он производить соответствующий хэш.
Конечно, в случае, если таблица паролей не будет взломана, мы должны разрешить хакерам, самое большее, десять попыток угадать 55-байтовый пароль пользователя, прежде чем заблокировать учетную запись пользователя;)
Если вы все же решили предварительно хешировать пароль длиной более 55 байт, вам следует использовать SHA-384, так как он дает наибольший результат без превышения лимита.
источник