Является ли хеширование пароля дважды перед хранением более или менее безопасным, чем простое хеширование?
То, о чем я говорю, делает это:
$hashed_password = hash(hash($plaintext_password));
вместо этого:
$hashed_password = hash($plaintext_password);
Если это менее безопасно, можете ли вы дать хорошее объяснение (или ссылку на него)?
Кроме того, используемая хэш-функция имеет значение? Есть ли какая-то разница, если вы смешаете md5 и sha1 (например) вместо того, чтобы повторять одну и ту же хеш-функцию?
Примечание 1: Когда я говорю «двойное хеширование», я говорю о хешировании пароля дважды, чтобы сделать его более скрытым. Я не говорю о технике разрешения коллизий .
Примечание 2: я знаю, что мне нужно добавить случайную соль, чтобы действительно обеспечить ее безопасность. Вопрос в том, помогает ли хеширование дважды с помощью одного и того же алгоритма или вредит хешу.
источник
Hash(password)
иHash(Hash(password))
одинаково небезопасны. У обоих отсутствует понятие семантической безопасности . То есть, выходной сигнал является отличимым от случайного. Например,MD5("password")
есть5f4dcc3b5aa765d61d8327deb882cf99
. Я знаю, что это хэш MD5password
, и его можно отличить от случайного. Вместо этого вы должны использовать HMAC. Это доказуемо безопасно и его PRF.Ответы:
Хеширование пароля один раз небезопасно
Нет, несколько хешей не менее безопасны; они являются неотъемлемой частью безопасного использования пароля.
Повторение хэша увеличивает время, которое требуется злоумышленнику для проверки каждого пароля в своем списке кандидатов. Вы можете легко увеличить время, необходимое для атаки на пароль, с часов до лет.
Простая итерация не достаточно
Простая цепочка вывода хеша для ввода недостаточна для безопасности. Итерация должна происходить в контексте алгоритма, который сохраняет энтропию пароля. К счастью, есть несколько опубликованных алгоритмов, которые были достаточно изучены, чтобы придать уверенность в их дизайне.
Хороший алгоритм получения ключей, такой как PBKDF2, вводит пароль в каждый раунд хэширования, уменьшая опасения по поводу коллизий при выводе хэша. PBKDF2 может использоваться для аутентификации по паролю как есть. Bcrypt следует за получением ключа с шагом шифрования; таким образом, если обнаружен быстрый способ обратить вспять процесс деривации ключа, злоумышленнику все равно придется выполнить атаку с использованием открытого текста.
Как взломать пароль
Сохраненные пароли нуждаются в защите от автономной атаки. Если пароли не засолены, их можно взломать с помощью заранее вычисленной атаки по словарю (например, с использованием таблицы Rainbow). В противном случае злоумышленник должен потратить время на вычисление хэша для каждого пароля и посмотреть, соответствует ли он сохраненному хешу.
Все пароли не одинаково вероятны. Злоумышленники могут провести тщательный поиск по всем коротким паролям, но они знают, что их шансы на успех грубой силой резко уменьшаются с каждым дополнительным персонажем. Вместо этого они используют упорядоченный список наиболее вероятных паролей. Они начинаются с «password123» и переходят к менее часто используемым паролям.
Допустим, список атакующих длинный, с 10 миллиардами кандидатов; Предположим также, что настольная система может вычислять 1 миллион хешей в секунду. Атакующий может проверить весь свой список менее чем за три часа, если используется только одна итерация. Но если используется всего 2000 итераций, это время увеличивается почти до 8 месяцев. Чтобы победить более искушенного злоумышленника - например, способного загрузить программу, которая может использовать мощь своего графического процессора, - вам нужно больше итераций.
Сколько достаточно?
Количество используемых итераций является компромиссом между безопасностью и опытом пользователя. Специализированное оборудование, которое может быть использовано злоумышленниками, дешево, но оно может выполнять сотни миллионов итераций в секунду. Производительность системы злоумышленника определяет, сколько времени потребуется, чтобы взломать пароль, учитывая количество итераций. Но ваше приложение вряд ли будет использовать это специализированное оборудование. Сколько итераций вы можете выполнить, не раздражая пользователей, зависит от вашей системы.
Вы, вероятно, можете позволить пользователям ждать больше ¾ секунды или около того во время аутентификации. Профилируйте свою целевую платформу и используйте столько итераций, сколько можете себе позволить. Платформы, которые я тестировал (один пользователь на мобильном устройстве или много пользователей на серверной платформе), могут с удобством поддерживать PBKDF2 с 60 000 и 120 000 итераций или bcrypt с коэффициентом стоимости 12 или 13.
Больше фона
Прочтите PKCS # 5 для получения достоверной информации о роли соли и итераций в хешировании. Несмотря на то, что PBKDF2 предназначался для генерации ключей шифрования из паролей, он хорошо работает как односторонний хэш для аутентификации по паролю. Каждая итерация bcrypt дороже, чем хеш SHA-2, поэтому вы можете использовать меньше итераций, но идея та же. Bcrypt также выходит за рамки большинства решений на основе PBKDF2, используя производный ключ для шифрования широко известного простого текста. Полученный зашифрованный текст сохраняется как «хэш» вместе с некоторыми метаданными. Однако ничто не мешает вам делать то же самое с PBKDF2.
Вот другие ответы, которые я написал на эту тему:
источник
Для тех, кто говорит, что это безопасно, они в целом верны . «Двойное» хеширование (или его логическое расширение, повторение хеш-функции) абсолютно безопасно, если все сделано правильно , для конкретной задачи.
Тем, кто говорит, что это небезопасно, они правы в этом случае . Код, который размещен в вопросе , небезопасен. Давайте поговорим о том, почему:
Есть два фундаментальных свойства хеш-функции, которые нас беспокоят:
Сопротивление перед изображением - учитывая хеш
$h
, должно быть трудно найти сообщение$m
, которое$h === hash($m)
Сопротивление второму изображению перед передачей - для данного сообщения
$m1
должно быть трудно найти другое сообщение$m2
, котороеhash($m1) === hash($m2)
Сопротивление столкновению - должно быть трудно найти пару сообщений,
($m1, $m2)
таких, которыеhash($m1) === hash($m2)
(обратите внимание, что это похоже на Сопротивление второго изображения, но отличается тем, что злоумышленник может контролировать оба сообщения) ...Для хранения паролей все, что нас действительно волнует, это Pre-Image Resistance . Два других будут спорными, потому
$m1
что это пароль пользователя, который мы пытаемся сохранить в безопасности. Так что, если у злоумышленника это уже есть, хешу нечего защищать ...ОТКАЗ ОТ ОТВЕТСТВЕННОСТИ
Все, что следует, основано на предпосылке, что все, о чем мы заботимся - это Сопротивление перед изображением . Два других фундаментальных свойства хеш-функций могут (и обычно не поддерживаются) одинаково. Поэтому выводы в этом посте применимы только при использовании хеш-функций для хранения паролей. Они не применимы в общем ...
Давайте начнем
Ради этого обсуждения давайте изобретем нашу собственную хеш-функцию:
Теперь должно быть совершенно очевидно, что делает эта хеш-функция. Он суммирует значения ASCII каждого входного символа, а затем принимает модуль этого результата с 256.
Итак, давайте проверим это:
Теперь давайте посмотрим, что произойдет, если мы запустим его несколько раз вокруг функции:
Что выводит:
Хм, вау. Мы создали столкновения !!! Давайте попробуем посмотреть, почему:
Вот результат хэширования строки каждого возможного хэша:
Обратите внимание на тенденцию к увеличению числа. Это оказывается нашим мертвым. Запуск хеша 4 раза ($ hash = ourHash ($ hash) `для каждого элемента) приводит к получению:
Мы сузили себя до 8 значений ... Это плохо ... Наша оригинальная функция отображена
S(∞)
наS(256)
. То есть мы создали Сюръекцию отображение$input
в$output
.Поскольку у нас есть функция Surjective, мы не можем гарантировать, что отображение для любого подмножества входных данных не будет иметь коллизий (фактически, на практике они будут).
Вот что здесь произошло! Наша функция была плохой, но это не то, почему это работало (вот почему это работало так быстро и полностью).
То же самое происходит с
MD5
. Он отображаетсяS(∞)
наS(2^128)
. Так как нет гарантии, что работаMD5(S(output))
будет Инъективной , это означает, что у нее не будет столкновений.Секция TL / DR
Следовательно, поскольку обратная передача выходных данных
md5
напрямую может привести к возникновению коллизий, каждая итерация увеличивает вероятность коллизий. Однако это линейное увеличение, что означает, что хотя набор результатов2^128
уменьшается, он не уменьшается значительно быстрее, чтобы стать критическим недостатком.Так,
Чем больше раз вы повторяете, тем дальше идет сокращение.
Исправление
К счастью для нас, есть тривиальный способ исправить это: передать что-то в дальнейшие итерации:
Обратите внимание, что дальнейшие итерации не 2 ^ 128 для каждого отдельного значения для
$input
. Это означает, что мы можем генерировать$input
значения, которые все еще сталкиваются по линии (и, следовательно, будут устанавливаться или резонировать при гораздо меньших, чем2^128
возможно, выходах). Но общий аргумент за$input
все еще так же силен, как это было для одного раунда.Подожди, это было? Давайте проверим это с помощью нашей
ourHash()
функции. Переключение$hash = ourHash($input . $hash);
на 100 итераций:Там все еще есть грубый паттерн, но обратите внимание, что это не более паттерн, чем наша основная функция (которая уже была довольно слабой).
Заметьте, однако, что
0
и3
стали коллизиями, хотя они не были в одном заезде. Это приложение из того, что я сказал ранее (что сопротивление столкновению остается неизменным для набора всех входов, но определенные маршруты столкновения могут открыться из-за недостатков в базовом алгоритме).Секция TL / DR
Возвращая входные данные в каждую итерацию, мы эффективно устраняем любые коллизии, которые могли произойти в предыдущей итерации
Следовательно,
md5($input . md5($input));
должно быть ( теоретически, по крайней мере) так же сильно, какmd5($input)
.Это важно?
Да. Это одна из причин того, что PBKDF2 заменил PBKDF1 в RFC 2898 . Рассмотрим внутренние циклы двух:
PBKDF1:
Где
c
счетчик итераций,P
пароль иS
сольPBKDF2:
Где PRF действительно просто HMAC. Но для наших целей, давайте просто скажем, что
PRF(P, S) = Hash(P || S)
(то есть, PRF 2 входных данных, грубо говоря, такой же, как хэш с двумя соединенными вместе). Это очень не так , но для наших целей это так.Таким образом, PBKDF2 поддерживает сопротивление столкновения базовой
Hash
функции, а PBKDF1 - нет.Связывая все это вместе:
Мы знаем о безопасных способах итерации хэша. По факту:
Обычно безопасно.
Теперь, чтобы понять, почему мы хотим его хешировать, давайте проанализируем движение энтропии.
Хэш принимает бесконечное множество:
S(∞)
и создает меньший, постоянно измеряемый наборS(n)
. Следующая итерация (при условии , входа передается обратно в) картахS(∞)
наS(n)
вновь:Обратите внимание, что конечный результат имеет точно такое же количество энтропии, что и первый . Повторение не «сделает его более затененным». Энтропия идентична. Нет волшебного источника непредсказуемости (это псевдослучайная функция, а не случайная функция).
Тем не менее, есть итерация. Это делает процесс перемешивания искусственно медленнее. И поэтому итерация может быть хорошей идеей. Фактически, это основной принцип большинства современных алгоритмов хеширования паролей (тот факт, что выполнение чего-то многократно делает это медленнее).
Замедление - это хорошо, потому что оно борется с основной угрозой безопасности: грубым принуждением. Чем медленнее мы создаем наш алгоритм хэширования, тем тяжелее атакующим приходится работать, чтобы атаковать хеши паролей, украденные у нас. И это хорошо !!!
источник
$output = md5($output); // < 2^128 possibilities
--- это действительно строгое<
, или<=
?md5()
в данном случае), чтобы действительно знать наверняка. Но в целом так будет<
и не<=
... Помните, речь идет о размере набора$output
для всех возможных$inputs
. Так что, если у нас есть еще одно столкновение будет<
, следовательно<
, тем лучше generalizer.Да, повторное хеширование уменьшает пространство поиска, но нет, это не имеет значения - эффективное сокращение незначительно.
Повторное хеширование увеличивает время, необходимое для грубой силы, но делать это только дважды - тоже неоптимально.
Что вам действительно нужно, так это хешировать пароль с помощью PBKDF2 - проверенного метода использования безопасного хэша с солью и итерациями. Проверьте этот SO ответ .
РЕДАКТИРОВАТЬ : Я почти забыл - не используйте MD5 !!!! Используйте современный криптографический хеш, такой как семейство SHA-2 (SHA-256, SHA-384 и SHA-512).
источник
Да - это уменьшает количество возможных строк, которые соответствуют строке.
Как вы уже упоминали, соленые хэши намного лучше.
Статья здесь: http://websecurity.ro/blog/2007/11/02/md5md5-vs-md5/ , пытается доказать, почему это эквивалентно, но я не уверен с логикой. Частично они предполагают, что не существует программного обеспечения для анализа md5 (md5 (текст)), но, очевидно, довольно просто создавать радужные таблицы.
Я все еще придерживаюсь своего ответа о том, что число хешей типа md5 (md5 (текст)) меньше, чем хешей md5 (текст), что увеличивает вероятность коллизии (даже если вероятность маловероятна) и уменьшает пространство поиска.
источник
Большинство ответов от людей, не имеющих опыта в криптографии или безопасности. И они не правы. Используйте соль, если возможно, уникальную для каждой записи. MD5 / SHA / etc слишком быстрые, в противоположность тому, что вы хотите. PBKDF2 и bcrypt медленнее (что хорошо), но их можно победить с помощью ASIC / FPGA / GPU (в настоящее время очень доступно). Таким образом, требуется жесткий алгоритм памяти: введите scrypt .
Вот объяснение дилетанта о солях и скорости (но не об алгоритмах с жесткой памятью).
источник
Я просто смотрю на это с практической точки зрения. Что за хакер после? Почему, комбинация символов, которая, когда передается через хеш-функцию, генерирует желаемый хеш.
Вы сохраняете только последний хеш, поэтому хакер должен обработать только один хеш. Предполагая, что у вас примерно одинаковые шансы споткнуться о желаемом хеше с каждым шагом грубой силы, количество хешей не имеет значения. Вы могли бы сделать миллион итераций хеша, и это не увеличило бы и не уменьшило бы безопасность на один бит, так как в конце строки остается только один хеш, и шансы его разрыва такие же, как и у любого хеша.
Может быть, предыдущие постеры считают, что вклад важен; это не. Пока все, что вы вводите в хеш-функцию, генерирует желаемый хеш, оно поможет вам получить правильный ввод или неправильный ввод.
Радужные столы - это уже другая история. Поскольку радужная таблица содержит только необработанные пароли, хэширование дважды может быть хорошей мерой безопасности, поскольку радужная таблица, содержащая каждый хэш каждого хеша, будет слишком большой.
Конечно, я рассматриваю только тот пример, который дал ОП, где просто хешируется простой текстовый пароль. Если вы включите имя пользователя или соль в хеш, это другая история; хеширование дважды совершенно не нужно, поскольку радужный стол уже будет слишком большим, чтобы быть практичным и содержать правильный хеш.
Во всяком случае, не эксперт по безопасности здесь, но это только то, что я понял из моего опыта.
источник
Из того, что я прочитал, на самом деле может быть рекомендовано повторно хэшировать пароль сотни или тысячи раз.
Идея состоит в том, что, если вы можете сделать так, чтобы шифрование пароля заняло больше времени, злоумышленнику придется пройти через множество предположений, чтобы взломать пароль. Это, кажется, является преимуществом для повторного хеширования - не потому, что оно более криптографически защищено, но для генерации атаки по словарю требуется больше времени.
Конечно, компьютеры становятся все быстрее, поэтому со временем это преимущество уменьшается (или требует увеличения количества итераций).
источник
Лично я не стал бы беспокоиться о множественных хэшах, но я бы также обязательно хэшировал имя пользователя (или другое поле идентификатора пользователя), а также пароль, чтобы два пользователя с одинаковым паролем не имели одинаковый хеш. Кроме того, я бы, вероятно, бросил какую-то другую константную строку во входную строку тоже для хорошей меры.
источник
Предположим, вы используете алгоритм хеширования: вычислите rot13, возьмите первые 10 символов. Если вы сделаете это дважды (или даже 2000 раз), можно создать функцию, которая работает быстрее, но дает тот же результат (а именно, просто взять первые 10 символов).
Точно так же можно создать более быструю функцию, которая дает тот же результат, что и повторяющаяся функция хеширования. Поэтому ваш выбор функции хеширования очень важен: как и в примере с rot13, не учитывается, что повторное хеширование повысит безопасность. Если нет исследований о том, что алгоритм предназначен для рекурсивного использования, то безопаснее предположить, что он не даст вам дополнительной защиты.
Тем не менее, для всех, кроме самых простых функций хеширования, скорее всего, потребуются эксперты по криптографии для вычисления более быстрых функций, поэтому, если вы защищаетесь от злоумышленников, не имеющих доступа к специалистам по криптографии, на практике, вероятно, безопаснее использовать функцию повторного хеширования ,
источник
Как правило, он не обеспечивает дополнительной безопасности для двойного хеширования или двойного шифрования чего-либо. Если вы можете разорвать хэш один раз, вы можете разорвать его снова. Обычно это не наносит вреда безопасности.
В вашем примере использования MD5, как вы, вероятно, знаете, есть некоторые проблемы коллизий. «Двойное хеширование» на самом деле не помогает защитить от этого, так как те же коллизии по-прежнему приводят к тому же первому хешу, который вы затем можете снова использовать MD5 для получения второго хеша.
Это защищает от атак по словарю, таких как «обратные базы данных MD5», но также и от соления.
С одной стороны, двойное шифрование чего-либо не обеспечивает никакой дополнительной безопасности, потому что все, что он делает, это приводит к другому ключу, который является комбинацией двух фактически используемых ключей. Таким образом, усилия по поиску «ключа» не удваиваются, потому что два ключа на самом деле не нужно искать. Это не верно для хеширования, потому что результат хеша обычно не совпадает с длиной исходного ввода.
источник
Двойное хеширование имеет смысл для меня, только если я хеширую пароль на клиенте, а затем сохраняю хеш (с другой солью) этого хеша на сервере.
Таким образом, даже если кто-то взломал его путь к серверу (игнорируя тем самым безопасность SSL), он все равно не сможет получить чистые пароли.
Да, у него будут данные, необходимые для проникновения в систему, но он не сможет использовать эти данные для компрометации внешних учетных записей, которые есть у пользователя. И люди, как известно, используют один и тот же пароль практически для чего угодно.
Единственный способ получить чистые пароли - это установить кейген на клиенте - и это больше не ваша проблема.
Итак, вкратце:
источник
Забота об уменьшении пространства поиска является математически правильной, хотя пространство поиска остается достаточно большим, что для всех практических целей (при условии, что вы используете соли), при 2 ^ 128. Тем не менее, поскольку мы говорим о паролях, число возможных 16-символьных строк (буквенно-цифровые, заглавные буквы, добавленные несколько символов) составляет примерно 2 ^ 98, согласно моим подсчетам за пределами конверта. Таким образом, воспринимаемое уменьшение в пространстве поиска не очень актуально.
Помимо этого, на самом деле нет никакой разницы, если говорить криптографически.
Хотя существует криптографический примитив, называемый «цепочкой хеширования» - методика, которая позволяет вам делать некоторые интересные трюки, такие как раскрытие ключа подписи после его использования, без ущерба для целостности системы - учитывая минимальное время синхронизации, это позволяет чисто обойти проблему первоначального распределения ключей. По сути, вы предварительно вычисляете большой набор хэшей хэшей - h (h (h (h .... (h (k)) ...))), используйте n-ное значение для подписи, после установленного интервала вы отправляете ключ и подпишите его, используя ключ (n-1). Получатели могут теперь проверить, что вы отправили все предыдущие сообщения, и никто не может подделать вашу подпись, так как прошел период времени, в течение которого она действительна.
Повторное хэширование сотен тысяч раз, как предлагает Билл, - просто пустая трата вашего процессора. Используйте более длинный ключ, если вы беспокоитесь о людях, ломающих 128 бит.
источник
Как показывают несколько ответов в этой статье, в некоторых случаях это может повысить безопасность, а в других - причинить вред. Есть лучшее решение, которое определенно улучшит безопасность. Вместо того, чтобы удваивать количество раз, которое вы вычисляете хеш, удваивайте размер вашей соли, или удваивайте количество бит, используемых в хэше, или делайте оба! Вместо SHA-245, прыгайте до SHA-512.
источник
Двойное хэширование ужасно, потому что, скорее всего, злоумышленник построил таблицу, чтобы получить большинство хэшей. Лучше солить свои хеши и смешивать их вместе. Существуют также новые схемы для «подписывания» хешей (в основном, соления), но более безопасным способом.
источник
Да.
Абсолютно не используйте несколько итераций обычной хеш-функции, например
md5(md5(md5(password)))
. В лучшем случае вы получите незначительное повышение безопасности (подобная схема практически не защищает от атак на GPU; просто конвейерная обработка). В худшем случае вы уменьшаете свое хэш-пространство (и, следовательно, безопасность) с каждой добавляемой итерацией , В безопасности разумно предположить худшее.Есть ли использовать пароль , который был разработан компетентным криптограф , чтобы быть эффективным хэш пароля, а также устойчив как к грубой силы и времени космического нападения. К ним относятся bcrypt, scrypt и, в некоторых случаях, PBKDF2. Хеш на основе glibc SHA-256 также приемлем.
источник
Я собираюсь выйти на конечность и сказать, что в определенных обстоятельствах она более безопасна ... но пока не понижайте меня!
С математической / криптографической точки зрения это менее безопасно по причинам, которые, я уверен, кто-то другой даст вам более четкое объяснение, чем я мог бы.
тем не мение существуют большие базы данных хэшей MD5, которые с большей вероятностью содержат текст «пароля», чем его MD5. Таким образом, двойное хеширование снижает эффективность этих баз данных.
Конечно, если вы используете соль, то это преимущество (недостаток?) Исчезнет.
источник