codedokode / Как солить и хешировать пароли.md
Здесь старая версия урока, которая больше не обновляется.
Итак, ты решил сделать авторизацию и регистрацию через пароли. Как максимально обезопасить пароли пользователей от взлома и от своих же любопытных сотрудников (если ты работаешь не один, а в большой компании)?
Соли @ хешируй
Для начала, никогда не храни открытые пароли. Храни соленые хеши от них. Хеш-функция, например md5, sha1 (про них написано в вики, почитай) — это практически необратимая функция. То есть получить хеш по паролю просто, а вот восстановить пароль, имея хеш практически невозможно — надо перебирать все возможные варианты паролей и сравнивать получившиеся хеши.
Какой смысл в хэше, если md5 все равно можно расшифровать? Пусть даже перебором?
Это займет много или очень много времени. Может, взломщик устанет ждать или пароли потеряют актуальность. Например, если хорошо шифровать, то годы (по идее там перебирать можно и 100 лет, но я думаю скоро изобретут какую-нибудь штуку для ускоренного перебора), вместо того чтобы взять и увидеть пароли в открытую.
Ок, достаточно ли использовать хеширование и хранить только хеши?
Нет! Без так называемой «соли» многие пароли можно подобрать за секунду если там использовать просто md5(pass). Не веришь? Читай ниже.
Что такое соль? Что значит «соленый хеш»?
Соль — случайно сгенерированная последовательность символов, которая хранится рядом с хешем. Дан пароль $pass = «123456» , мы генерируем случайную соль например $salt = ‘A&%6t*(k:’ и получаем хеш от «соль + пароль»: $hash = md5($salt . $pass) . В базу сохраняется отдельно использованная соль (она для каждого пользователя своя), отдельно хеш.
Теперь попробуем применить математику и посчитать насколько надежны разные способы хеширования. Сейчас подбор пароля делается 2 способами:
Способ 1
Перебираем все возможные пароли, начиная например с 1111111 и заканчивая zzzzzzz и вычисляем от каждого md5-хеш. При этом число вариантов, которые надо подобрать, зависит от длины и набора символов (чем их больше тем больше перебирать). Скорость перебора md5 на топовых видеокартах составляет около 10 миллиардов в секунду ( http://www.opennet.ru/opennews/art.shtml?num=30201 и http://hashcat.net/oclhashcat/ ). А ведь можно взять не одну видеокарту, а много, если очень надо.
Заметь, что если у тебя база с кучей хешей, то их все можно проверять их все одновременно примерно с такой же скоростью как и один хеш.
Считаем число вариантов.
36^6 — значит 36 в 6-й степени, то есть 36*36*36*36*36*36 если что.
- Если в пароле 12 цифр 0-9 : число комбинаций = 10^12 = 1000 миллиардов = 100 секунд перебора на 1 видеокарте (1 секунда на 100 карточках параллельно).
- Если 6 букв a-z или цифр 0-9 . Число вариантов = 36^6 (считаем гуглом) = 2 млрд. Хехе, меньше секунды.
- Если 6 букв a-zA-Z (добавим маленькие и большие буквы) и 0-9 . Комбинаций 62^6 = 56 млрд. 6 секунд перебора.
- Если в пароле 8 букв a-zA-Z и цифр 0-9 . Комбинаций уже 62^8 = 218 триллионов. Это 22000 секунд перебора (в часе 3600 секунд, так что выходит 6 часов) на 1 карточке или 220 секунд на 100 карточках. Ого, не очень-то надежно.
- Если в пароле 10 символов a-zA-Z0-9 + 20 знаков вроде минус, плюс.. то выходит 82^10 комбинаций ~ 10^19 и перебирать их 10^9 секунд на одной карте (11500 дней) или 115 дней на сотне карточек.
Люди часто ставят паролем не бредовый набор букв, а слова или куски слов. Значит, какие-то символы рядом встречаются чаще, их можно перебирать в первую очередь тем самым сокращая число вариантов и ускоряя время нахождения.
В общем, видишь, без добавления соли пароли подберутся на раз. И не все же ставят 10-символьные пароли, у многих там просто слово или цифры.
Способ 2
Есть еще другой вариант — скачать огромные радужные таблицы (читай в вики про них) где хранятся уже рассчитанные цепочки хешей (для простых паролей). И конечно все хеши от обычных паролей длиной до 10 символов там уже есть (больше нету, так как они начинают занимать гигабайты. Но это вопрос времени, когда жесткие диски станут больше). Если ты хранишь в базе md5(pass) она вскроется мигом. Таблицы можно скачать тут: https://www.freerainbowtables.com/en/tables2/ (если не открывается, выбери английский язык и открой ссылку еще раз).
Вот пример такой таблицы: md5_loweralpha-numeric#1-10 588 GB — подбирает пароли без соли до 10 символов [a-z0-9].
Заметь что в будущем компьютеры будут мощнее, и значит подбираться пароли будут быстрее. Теперь подумаем как защититься и усложнить жизнь взломщикам:
- разрешаем использовать больше видов символов в паролях
- добавляем соль. С солью не получится параллельно подбирать все хеши так как у каждого юзера соль своя и каждый хеш надо перебирать отдельно, что сильно замедляет взлом. Также, при добавлении соли даже к простому паролю он по сути становится длинным и сложным и его не будет в радужных таблицах (123456 → Y^juYUHkd%$fdtd123456). Опять же, соль должна быть подлиннее и содержать спецсимволы чтобы было больше комбинаций для перебора. Ну конечно, простые пароли типа 123456 все равно вскроют, так как их при переборе проверяют в первую очередь. А вот сложные придется подбирать долго.
- используем вместо md5 более тяжелые для вычисления алгоритмы вроде bcrypt, который сделан так, что его нельзя перебрать быстрее чем за опредеенное время (и ты можешь указать требемый уровень сложности).
С правильным подходом даже простой md5 замучаешься расшифровывать.
Зачем нужна соль при хешировании
Очень часто можно встретить различного рода соль при хешировании паролей.
Как она выглядит?
Соль это набор случайно подобранных символов, который конкатенируется с паролем при хешировании. Обычно конструкция выглядит где-то так:
$salt = '&WsWЗачем она все таки нужна?
Криптографическая соль нужна только для одной цели: защита от радужных таблиц. Дублируя википедию, скажу что радужная таблица — это набор готовых (предрассчитанных) хешей и их оригинальных значений. Такие таблицы генерируются обычным брут форсом, часто по словарю (Пример: http://md5decrypt.net). Быстрота определения оригинальной фразы по хешу осуществляется индексированным поиском по всей базе.
Зачастую пароли пользователей очень просты — словарь очень узок и часто пользуются шаблонные слова и словосочетания. В случае, если соль не была добавлена к оргинальному паролю, есть большая вероятность что такой хеш уже скомпрометирован (например хеш от «mypassword» 34819D7BEEABB9260A5C854BC85B3E44 уже есть в базе данных md5decrypt.net).
В случае добавления соли, особенно если она находится в окружении сервера и никогда не покидает его пределы (не публична), мы получаем широкий исходный алфавит и большую длинну исходного значения.
Какие к ней требования?
Требования к соли следующие:
- приватную соль не ставить соль в начало фразы, поскольку в таком случае возможна атака удлиннением сообщения
- соль должна иметь достаточную длину, хотя бы 10 символов, больше — лучше.
- алфавит лучше использовать из всех возможных символов.
2030039 — не очень хорошая соль
Соль
Соль (salt) в информационной безопасности - это случайная дополнительная информация, которая добавляется к паролю перед его хешированием. Использование соли повышает безопасность пароля и усложняет его подбор злоумышленниками.
При хешировании пароля без соли злоумышленники могут использовать радужные таблицы (rainbow tables) для быстрого подбора пароля, так как хеш каждого пароля будет одинаковым. Однако, если к паролю добавляется уникальная соль, то хеш будет уникальным для каждого пароля, даже если сами пароли идентичны. Это делает задачу взлома паролей намного сложнее для злоумышленников.
Соль может содержать любые символы, включая буквы, цифры и специальные символы.
Безопасное хеширование паролей
В этом разделе разъясняются причины, стоящие за хешированием паролей в целях безопасности, а также эффективные методы хеширования.
- Почему я должен хешировать пароли пользователей в моем приложении?
- Почему популярные хеширующие функции, такие как md5 и sha1 не подходят для паролей?
- Если популярные хеширующие функции не подходят, как же я тогда должен хешировать свои пароли?
- Что такое соль?
- Как я должен хранить свою соль?
Почему я должен хешировать пароли пользователей в моем приложении?
Хеширование паролей является одним из самых основных соображений безопасности, которые необходимо сделать, при разработке приложения, принимающего пароли от пользователей. Без хеширования, пароли, хранящиеся в базе вашего приложения, могут быть украдены, например, если ваша база данных была скомпрометирована, а затем немедленно могут быть применены для компрометации не только вашего приложения, но и аккаунтов ваших пользователей на других сервисах, если они не используют уникальных паролей.
Применяя хеширующий алгоритм к пользовательским паролям перед сохранением их в своей базе данных, вы делаете невозможным разгадывание оригинального пароля для атакующего вашу базу данных, в то же время сохраняя возможность сравнения полученного хеша с оригинальным паролем.
Важно заметить, однако, что хеширование паролей защищает их только от компрометирования в вашем хранилище, но не обязательно от вмешательства вредоносного кода в вашем приложении.
Почему популярные хеширующие функции, такие как md5() и sha1() не подходят для паролей?
Такие хеширующие алгоритмы как MD5, SHA1 и SHA256 были спроектированы очень быстрыми и эффективными. При наличии современных технологий и оборудования, стало довольно просто выяснить результат этих алгоритмов методом "грубой силы" для определения оригинальных вводимых данных.
Из-за той скорости, с которой современные компьютеры могут "обратить" эти хеширующие алгоритмы, многие профессионалы компьютерной безопасности строго не рекомендуют использовать их для хеширования паролей.
Если популярные хеширующие функции не подходят, как же я тогда должен хешировать свои пароли?
При хешировании паролей существует два важных соображения: это стоимость вычисления и соль. Чем выше стоимость вычисления хеширующего алгоритма, тем больше времени требуется для взлома его вывода методом "грубой силы".
Другой возможностью является функция crypt() , которая поддерживает несколько алгоритмов хеширования. При использовании этой функции вы можете быть уверенным, что выбранный вами алгоритм доступен, так как PHP содержит собственную реализацию каждого поддерживаемого алгоритма, даже в случае, если какие-то из них не поддерживаются вашей системой.
При хешировании паролей рекомендуется применять алгоритм Blowfish, который также используется по умолчанию в API хеширования паролей, так как он значительно большей вычислительной сложности, чем MD5 или SHA1, при этом по-прежнему гибок.
Учтите, что, если вы используете функцию crypt() для проверки пароля, то вам нужно предостеречь себя от атак по времени, применяя сравнение строк, которое занимает постоянное время. Ни операторы PHP == и ===, ни функция strcmp() не являются таковыми. Функция же password_verify() как раз делает то, что нужно. Настоятельно рекомендуется использовать встроенное API хеширования паролей, если есть такая возможность.
Что такое соль?
Криптографическая соль представляет собой данные, которые применяются в процессе хеширования для предотвращения возможности разгадать оригинальный ввод с помощью поиска результата хеширования в списке заранее вычисленных пар ввод-хеш, известном также как "радужная" таблица.
Более простыми словами, соль - это кусочек дополнительных данных, которые делают ваши хеши намного более устойчивыми к взлому. Существует много онлайн-сервисов, предоставляющих обширные списки заранее вычисленных хешей вместе с их оригинальным вводом. Использование соли делает поиск результирующего хеша в таком списке маловероятным или даже невозможным.
password_hash() создаёт случайную соль в случае, если она не была передана, и чаще всего это наилучший и безопасный выбор.
Как я должен хранить свою соль?
При использовании функции password_hash() или crypt() , возвращаемое значение уже содержит соль как часть созданного хеша. Это значение нужно хранить как есть в вашей базе данных, так как оно содержит также информацию о хеширующей функции, которая использовалась, и может быть непосредственно передано в функции password_verify() или crypt() при проверке пароля.
Следующая диаграмма показывает формат возвращаемого значения функциями crypt() или password_hash() . Как можно видеть, они содержат полную информацию об алгоритме и соли, требуемых для будущей проверки пароля.