Что такое соль в хешировании
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Open with Desktop
- View raw
- Copy raw contents Copy raw contents
Copy raw contents
Copy raw contents
Как безопасно хранить пароли
Итак, мы решили сделать авторизацию и регистрацию на сайте через пароли. Как максимально обезопасить пароли пользователей от взлома, от хостинговой компании, которой принадлежит сервер, и от своих же любопытных коллег, имеющих доступ к базе?
Солить и хешировать
Для начала, никогда не храните пароли в открытом виде. Храните соленые хеши от них.
Хеш-функция - это такая функция, которая принимает на вход произвольную строку (например, пароль) и выдает на выходе хеш - число или строку небольшой фиксированной длины, из которой невозможно восстановить исходные данные. Криптографическая хеш-функция отличается от обычной защитой от манипуляций, например, она не позволяет после изменения строки добавить несколько символов, чтобы получить такой же хеш, который был у исходной строки (обычные хеш-функции вроде CRC32 используются только для защиты от случайных ошибок, а не от умышленных воздействий).
Примерами криптографических хеш-функций являются, например, MD5, SHA256 (про них написано в вики, но предупрежу, что понять их алгоритм без знания основ криптографии будет непросто). Способы легко обратить хеш-функцию и получить из хеша исходную строку неизвестны. То есть получить хеш из пароля просто, а вот восстановить пароль, имея хеш практически невозможно — надо перебирать все возможные пароли, вычислять для каждого хеш и сравнивать с имеющимся.
Вот пример хешей от пароля 'strongpassword': md5('strongpassword') = f93fc10472a31bb3061aa0b45e228c5a , sha1('strongpassword') = 2ae868079d293e0a185c671c7bcdac51df36e385 . Здесь хеши записаны в 16-чной системе счисления с помощью символов 0-9, a-f.
Итак, если вместо пароля хранить его хеш, то мы по-прежнему можем проверить, правильный ли пароль ввел пользователь (получив его хеш и сравнив с тем, что хранится в базе), но не можем получить исходный пароль. Однако, просто хеширования недостаточно и этот подход имеет такие недостатки:
- если у двух пользователей одинаковые пароли, то и хеши у них будут одинаковые
- пользователи часто выбирают простые пароли, и у злоумышленника может быть заготовлена таблица хешей от популярных паролей вроде '123456'
И есть еще один, самый главный недостаток - все хеши можно подбирать одновременно. Допустим, злоумышленник украл базу с хешами паролей. Он начинает их подбирать, перебирая все возможные пароли, вычисляя для каждого хеш и сравнивая с украденной базой. Проблема в том, что все пароли перебираются по сути одновременно - злоумышленник нашел хеш для пароля '1', сравнил его со всеми хешами в базе, и за один шаг узнал, есть ли в базе такой пароль или нет.
Для борьбы с этими недостатками используют "соление" паролей перед хешированием. При регистрации пользователя генерируется соль (salt) - случайный набор символов вроде H*5$@)_-hPoI&^530 . Соль не видна пользователю, потому она может быть сложной и длинной. Затем мы присоединяем соль к паролю, для пароля 123456 в итоге получается строка H*5$@)_-hPoI&^530:123456 . И затем уже от этой строки берем хеш и сохраняем в базу соль и хеш.
Благодаря добавлению соли даже одинаковые пароли получают разные хеши (так как у них разная соль), а таблицы заранее вычисленных хешей для популярных паролей становятся бесполезными. И атакующий теперь при переборе вынужден подбирать пароль для каждого хеша индивидуально, что сильно замедляет работу.
Для удобства хранения многие функции объединяют хеш пароля и соль, которая использовалась при хешировании, в одну строку, например такого формата: соль$хеш . Таким образом функция хеширования пароля может вернуть сразу и хеш, и сгенерированную ей соль.
Встроенные в PHP криптографические функции хеширования
В PHP5.5 и новее
В PHP5.5 сделали стандартный набор функций для работы с паролями, среди которых есть:
-
- генерирует соль и возвращает эту соль и хеш для данного пароля - используется для проверки пароля, принимает на вход пароль и соленый хеш и проверяет, соответствует ли пароль хешу
Функция password_hash возвращает строку, которая содержит сразу хеш, соль и обозначение использованного алгоритма хеширования, так что для их хранения достаточно одной ячейки в базе данных. Подробнее:
-
- генерирует хеш с использованием алгоритма MD5. Хеш состоит из 32 символов из набора [0-9a-f] - генерирует хеш с использованием алгоритма SHA-1, возвращает хеш из 40 символов из набора [0-9a-f] содержит функции хеширования для различных алгоритмов
- Функция openssl_digest из расширения openssl позволяет хешировать данные различными алгоритмами
Чтобы сгенерировать соль, необходим надежный (непредсказуемый) криптографический генератор случайных чисел. В качестве него можно использовать:
- добавленную в PHP7 функцию random_bytes() из расширения SSL, при этом важно прочитать документацию и убедиться, что используется надежный алгоритм
- на linux/mac можно читать случайные данные из /dev/random
Оценка сложности подбора пароля, зная хеш
Перебор без соли
Заметим, что из-за отсутствия соли мы подбираем пароли для всех хешей в базе параллельно, с примерно такой же скоростью, как и для одного хеша.
Если пароль состоит из N символов, и всего использованы M различных видов символов, то число возможных вариантов паролей, которые придется перебрать, равняется M N (M в степени N). Например:
- если в пароле 12 цифр 0-9 : число комбинаций = 10 12 = 1000 миллиардов = 500 секунд перебора на 1 видеокарте.
- если в пароле 6 букв a-z или цифр 0-9 . Число вариантов = 36 6 (считаем гуглом) = 2 млрд. Около секунды.
- если в пароле 6 букв a-zA-Z (добавим буквы в разном регистре) и цифр 0-9 . Комбинаций 62 6 = 56 млрд., или 28 секунд перебора.
- если в пароле 8 букв a-zA-Z и цифр 0-9 . Комбинаций уже 62 8 = 218 триллионов. Это примерно 109000 секунд перебора (в часе 3600 секунд, так что выходит 30 часов) на 1 карточке.
- если в пароле 10 символов из набора a-zA-Z0-9 или дополнительных 20 знаков вроде минус, плюс, то выходит 82 10 комбинаций ~ 10 19 и перебирать их 5×10 9 (5 миллиардов) секунд на одной карте (57870 дней, или 158 лет) или 58 дней на массиве из тысячи видеокарт.
Люди часто ставят паролем не случайный набор букв, а слова или куски слов. Значит, какие-то символы рядом встречаются чаще, их можно перебирать в первую очередь, тем самым сокращая число вариантов и ускоряя время нахождения. У злоумышленников есть словари, а также огромные списки паролей, полученные ими из предыдущих взломов и утечек.
В общем, видно, что без добавления соли пароли подберутся на раз. И не все ставят 10-символьные пароли, у многих там просто слово или цифры.
В случае добавления соли указанное время выше подбора будет относиться к подбору одного хеша, а не ко всей базе.
Другой вариант — сгенерировать или скачать огромные радужные таблицы, где хранятся уже рассчитанные цепочки хешей (для простых паролей). И конечно все хеши от обычных паролей длиной до 10 символов там уже есть (больше нету, так как они начинают занимать гигабайты. Но это вопрос времени, когда жесткие диски станут больше). Если хранить в базе хеш без соли, то взлом будет очень быстрым.
Заметим что в будущем компьютеры будут мощнее, и значит подбираться пароли будут быстрее. Теперь подумаем, как защититься и усложнить жизнь взломщикам:
Хабр не место для репостов и копи-паст — запомнил!
Это репост комментария упомянутого в хабрпосте, а сам комментарий является комментарием (простите за тавтологию) вот на эту статью, где и может быть найден. На мой взгляд ему место на хабре, потому-что он шедеврален! Это должен прочитать каждый программист и DBA, да и вообще полезен для любого %username%.
Добавлять по очереди то соль, то воду, то ещё чего, можно до бесконечности… Не вижу смысла.
Первая проблема — это, короткий, «не извращённый» (и часто состоящий только из цифр) пароль пользователя. Взлом заключается в сравнении украденного хеша и хеша из «радужной таблицы». Размеры таблицы — не бесконечны. Скажем при переборе всех паролей из 6 символов (т. е. перепробовав все возможные символы в таком пароле, например 0-9, a-z, A-Z, всего 62 символа) получаем 62^6 (62 символа в 6-ой степени) = 56'800'235'584 хешей. Каждый длиной 32 байта, значит места на диске для хранения таблицы нужно 56'800'235'584 * 32 = 1'817'607'538'688 т. е. примерно 2 Тбайта. Это не очень много, но и пароль у нас был длиной всего в 6 символов, и именно такие короткие пароли (ну или чуть длиннее 9 -12 символов, но тогда генерируются и хранятся хеши паролей по словарю, т. е. по списку чаще используемых юзерами паролей, это обычные слова, цифры ну и т. д., т. е. не «извращённые» пароли). А теперь посчитайте сколько хешей будет при длине пароля в 32 символа. Но заставлять юзера вводить и помнить пароль из 32-ух символов, конечно же не реально, поэтому просто-напросто прогоняем реальный пароль юзера 2 раза, например функцией md5();. После первого прогона мы получаем пароль, при чём не простой, а «извращённый», и длиной в 32 байта. А при втором прогоне, уже пароля из 32-х символов, получаем новый хеш, который и сохраняем. Теперь, что бы путём перебора определить из имеющегося хеша предыдущий хеш, а за тем по найденному предыдущему хешу — реальный пароль юзера, нужна будет, мягко говоря, «бесконечная» таблица хешей…
Вот так, примерно!
Но это всё была присказка. а сказка — впереди…
Теперь возвращаемся к нашей главной проблеме, КОРОТКИЕ И ПРОСТЫЕ ПАРОЛИ юзеров.
Что мне нужно, что бы «взломать» выше описанный способ «хранения» паролей. А главное — что я уже ИМЕЮ для этого?
А имею я совсем НЕ МАЛО! Можно сказать, что у меня уже есть пароль юзера. А точнее говоря — у меня уже есть все пароли большинства юзеров. А почему? да всё потому же! «короткие и простые пароли юзеров».
Теперь, что я делаю…
1. Я регюсь на взламываемом сайте.
2. Взламываю БД или то место, где хранятся хеши паролей. (это условие данной темы)
3. Нахожу свой хеш. (по моему имени юзера)
-. Зачем мне нужны первые три шага?
+. Для того, чтобы определить, каким образом получают хеш на сайте. Т. е. я перебираю возможные варианты:
md5($pass);
md5(md5($pass));
md5(md5(md5($pass)));
sha1(md5(crypt($pass)));
… и т. д., пока не получу мой хеш!
Если программер использовал просто md5(md5($pass));, то мне легче.
4. «Формула» получения хеша у меня есть, теперь мне нужна прога которая сгенерирует
все хеши ПО ДАННОЙ формуле (скачаю или сам напишу), и не много времени (если в секунду 1'000'000 хешей, то для 56'800'235'584 хешей это около 20 часов, НО это считайте МАКСИМУМ, а если по словарю перебирать или только пароли из цифр, то времени на порядок меньше потребуется).
И ВСЁ! Все пароли длиной до 6-и символов у меня «в кармане»!
И так! Этот метод взломали, теперь ПРО СОЛЬ…
Ломаем метод автора статьи…
-. Выполняю первые 3 шага.
Теперь, если я взломал БД и получил хеши паролей, то я также и получил каждую «солинку».
И что я делаю?
Да всё тоже самое.
Просто теперь при поиске «формулы» получения хеша я добавляю эту соль, при чём всеми возможными вариантами!
md5(md5($pass.$salt));
md5(md5($pass).$salt);
md5(sha1($salt.crypt($pass)));
… и т. д…
Ну а далее думаю уже догадываетесь… Генерю все хеши добавляя соль УЖЕ в правильное место и используя правильную формулу.
НО здесь как видите уже есть один «худенький» плюсик.
Речь уже идёт не о взломе всех паролей, а о взломе одиночного аккаутна, соль то для каждого юзера своя, а значит генерировать таблицу придётся для каждого юзера заново. Во как!
А почему плюсик «худенький»?
Да опять же всё потому, что «ПРОСТЫЕ И КОРОТКИЕ ПАРОЛИ». (на верное я вам уже надоел. терпи'те!)
Начинаю генерить по словарю используя только цифры (большинство паролей — это даты рождения). Обычно на сайте требуют пароль длиной не менее 6-и символов, т. е. я перебираю например:
даты типа 010101; (длина 6 символов, всего их от 01 января 1901 года, до 22 апреля 2011 где то 365*110=36500. ВСЕГО ТО ?! и это я ещё щедрый, а можно смело убрать первые лет 50 и последние лет 10-15.
Так же, даты когда юзер пишет что то типа 111977; (1 января 1977 года, т. е. варианты без нулей)
«Год у меня был… 3 — за побег… 5 — за дет-сад… ну сколько за старуха дадут? ну пусть 10 лет… И я из-за каких-то 16 лет. »
Ну пусть у нас получилось всего 1'000'000 вариантов даты рождения! Если машина генерирует 1'000'000 хешей в секунду — получается я буду вскрывать по юзеру в секунду, а если двигаться от младших к старшим, то ещё быстрее.
И что у нас получилось? Мы вскрыли за один час — 3600 юзеров «с СОЛЬЮ»!
И вся прелесть в том, что и соль НЕ ПОМОГЛА! А почему? Вижу по лицу, уже догадались!
последний раз: «ПРОСТЫЕ И КОРОТКИЕ ПАРОЛИ».
«И что же?» — скажете вы — «значит нет надёжного метода?».
И ПРАВИЛЬНО скажете!
Вы ищете в интернете надёжный метод или как вы ещё любите «часто используемый метод», и при этом вы УЖЕ СОВЕРШАЕТЕ ОШИБКУ! потому, как «то, что знают двое — знают все!» и как вы знаете «то, что один человек построил — другой завсегда поломать сможет».
Ну так и что же делать?
А всё просто, «ХОЧЕШЬ ЖИТЬ — УМЕЙ ВЕРТЕТЬСЯ!»
Не используйте общеизвестные приёмы, или изменяйте их на свой манер, отпиливайте, приклеивайте, меняйте местами, копируйте, придумывайте что то своё, и т. д. и т. п… Думайте своей головой. И вообще, подумайте, а стоит ли овчина выделки. Нужна ли вам эта бетонная крепость, или можно и и так прожить в деревянной… Даже если вы напишете код, который будет чередовать функции хеширования 10-20 раз, например в файле «enter.php». Во первых: что мне помешает написать программку которая будет перебирать все варианты чередования функций md5, sha1, crypt, и т.д., и в итоге будет находить нужную последовательность за считанные секунды. А во вторых: где гарантия, что я не заставлю сервер не выполнить ваш файл «enter.php», а просто прочитать его, или найти в вашем сайте ещё какую дырку и получить исходный код файла. И тогда хоть ваш код чередует 1000 раз, да и всё что угодно, я просто повторю ваш код при генерации таблицы хешей и результат будет тот же, а все ваши старания понапрасну…
Так что, надёжной защиты нет. Бывает лишь более надёжная защита, и бывает хакер который хитрее Вас, кстати, который не обязательно умнее Вас!
P.S. комментарий оставлен неким «k313», возможно этим, если автор оригинального комментария против данного репоста, я уберу.
P.S.S. при данном репосте из оригинального комментария убраны лишние знаки препинания.
Безопасность всегда была неоднозначной темой, провоцирующей многочисленные горячие споры. И всё благодаря обилию самых разных точек зрения и «идеальных решений», которые устраивают одних и совершенно не подходят другим. Я считаю, что взлом системы безопасности приложения всего лишь вопрос времени. Из-за быстрого роста вычислительных мощностей и увеличения сложности безопасные сегодня приложения перестанут завтра быть таковыми.
- Односторонняя функция: из хэша невозможно восстановить исходные данные с помощью какого-либо эффективного алгоритма.
- Конвертация данных переменной длины в данные фиксированной длины: входное значение может быть «бесконечной» длины, а выходное — нет. Это подразумевает, что два или более входных значения могут иметь одинаковые хэши. Чем меньше длина хэша, тем выше вероятность коллизии.
Тот факт, что с помощью эффективного алгоритма невозможно провести операцию, обратную хэшированию, и восстановить исходные данные, не означает, что вас не могут взломать. Если хорошо поискать, то можно найти базы данных с хэшами распространённых слов и коротких фраз. Кроме того, простые пароли можно быстро и легко брутфорсить или взламывать перебором по словарю.
Вот небольшая демонстрация, как инструмент sqlmap через внедрение SQL-кода взламывает пароли с помощью брутфорса хэшей, сгенерированных алгоритмом MD5.
Чтобы затруднить атаки описанного вида, применяется так называемая соль. Это стандартное средство, но в условиях современных вычислительных мощностей его уже недостаточно, особенно если длина соли невелика.
В общем виде функцию с использованием соли можно представить так:
f(password, salt) = hash(password + salt)
Для затруднения брутфорс-атаки соль должна быть длиной не менее 64 символов. Но проблема в том, что для дальнейшей аутентификации пользователей соль должна храниться в БД в виде простого текста.
if (hash([введённый пароль] + [соль]) == [хэш]) тогда пользователь аутентифицирован
Благодаря уникальности соли для каждого пользователя мы можем решить проблему коллизий простых хэшей. Теперь все хэши будут разными. Также уже не сработают подходы с гугленьем хэшей и брутфорсом. Но если злоумышленник через внедрение SQL-кода получит доступ к соли или БД, то сможет успешно атаковать брутфорсом или перебором по словарю, особенно если пользователи выбирают распространённые пароли (а-ля 123456).
Тем не менее взлом любого из паролей уже не позволит автоматически вычислить пользователей, у которых тот же пароль, — ведь у нас ВСЕ хэши разные.
Для генерирования подходящей соли нам нужен хороший генератор случайных чисел. Сразу забудьте о функции rand().
Есть замечательная статья, посвящённая этому вопросу. Вкратце: компьютер сам по себе не генерирует случайные данные, это детерминированная машина. То есть каждый выполняемый алгоритм, получив несколько раз на входе одни и те же данные, на выходе представит один и тот же результат.
Когда от компьютера хотят случайное число, то обычно он берёт данные из нескольких источников (например, переменные среды: дату, время, количество записанных/считанных байтов и т. д.), а затем производит над ними вычисления для получения «случайных» данных. Поэтому такие данные называют псевдослучайными. А значит, если каким-то образом воссоздать набор исходных состояний на момент исполнения псевдослучайной функции, то мы сможем сгенерировать то же самое число.
Если псевдослучайный генератор ещё и реализован неправильно, то в генерируемых им данных можно обнаружить паттерны, а с их помощью предсказать результат генерирования. Взгляните на эту картинку, представляющую собой результат работы PHP-функции rand():
А теперь сравните с данными, сгенерированными полноценным генератором случайных чисел:
К сожалению, ни rand(), ни mt_rand() нельзя считать подходящими инструментами для обеспечения высокого уровня безопасности.
Если вам нужно получить случайные данные, воспользуйтесь функцией openssl_random_pseudo_bytes(), которая доступна начиная с версии 5.3.0. У неё даже есть флаг crypto_strong, который сообщит о достаточном уровне безопасности.
Можно внедрить растяжение пароля, это позволяет ещё больше затруднить брутфорс-атаки. Растяжение представляет собой итеративный, или рекурсивный, алгоритм, который раз за разом вычисляет хэш самого себя, десятки тысяч раз (а то и более).
Количество итераций должно быть таково, чтобы общее время вычислений заняло как минимум одну секунду. Чем более длительное получается хэширование, тем больше времени атакующему приходится тратить на взлом.
- знать точное количество итераций, поскольку любое отклонение будет давать другой хэш;
- ждать не менее секунды между каждой попыткой.
Для растяжения пароля можно использовать стандартные алгоритмы, например PBDKDF2, представляющий собой функцию формирования ключа:
Есть и более затратные по времени и памяти алгоритмы, например bcrypt (о нём мы поговорим ниже) или scrypt:
- $cost — коэффициент трудоёмкости;
- $salt — случайная строка. Её можно генерировать, например, с помощью описанной выше функции secure_rand().
На данный момент в PHP не реализована поддержка алгоритма scrypt, но можно воспользоваться реализацией от Domblack.
Многие путаются в терминах «хэширование» и «шифрование». Как было упомянуто выше, хэш — результат работы псевдослучайной функции, в то время как шифрование — осуществление псевдослучайного преобразования: входные данные делятся на части и обрабатываются таким образом, что результат становится неотличим от результата работы полноценного генератора случайных чисел. Однако в этом случае можно провести обратное преобразование и восстановить исходные данные. Преобразование осуществляется с помощью криптоключа, без которого невозможно провести обратное преобразование.
Необходимо уделять большое внимание правильности использования шифрования. Не думайте, что для защиты важных данных достаточно просто зашифровать по какому-нибудь алгоритму. Есть немало способов украсть данные. Главное правило — никогда не занимайтесь самодеятельностью и пользуйтесь уже готовыми, отработанными реализациями.
Некоторое время назад у Adobe была мощная утечка пользовательской БД из-за неправильно реализованного шифрования. Давайте разберём, что у них произошло.
Предположим, что в таблице хранятся следующие данные в виде обычного текста:
- использовал один и тот же криптоключ;
- оставил поля passwordHint незашифрованными.
Мы не знаем, какой применялся криптоключ. Но если проанализировать данные, то можно заметить, что в строках 2 и 7 используется один и тот же пароль, так же как и в строках 3 и 6.
Пришло время обратиться к подсказке пароля. В строке 6 это «I’m one!», что совершенно неинформативно. Зато благодаря строке 3 мы можем предположить, что пароль — queen. Строки 2 и 7 по отдельности не позволяют вычислить пароль, но если проанализировать их вместе, то можно предположить, что это halloween.
Ради снижения риска утечки данных лучше использовать разные способы хэширования. А если вам нужно шифровать пароли, то обратите внимание на настраиваемое шифрование:
Пусть у нас есть тысячи пользователей и мы хотим зашифровать все пароли. Как было показано выше, лучше избегать использования единого криптоключа. Но и сделать для каждого пользователя уникальный ключ мы тоже не можем, поскольку хранение ключей само по себе превратится в проблему. В этом случае достаточно применять общий для всех криптоключ, но при этом делать «настройку», уникальную для каждого пользователя. Комбинация ключа и «настройки» и будет являться уникальным ключом для каждого пользователя.
Простейший вариант «настройки» — так называемый первичный ключ, уникальный для каждой записи в таблице. Не рекомендуется пользоваться им в жизни, здесь он показан лишь для примера:
f(key, primaryKey) = key + primaryKey
Здесь ключ и первичный ключ просто сцепляются вместе. Но для обеспечения безопасности следует применить к ним алгоритм хэширования или функцию формирования ключа (key derivation function). Также вместо первичного ключа можно для каждой записи использовать одноразовый ключ (аналог соли).
Если мы применим к нашей таблице настраиваемое шифрование, то она будет выглядеть так:
Конечно, нужно будет ещё что-то сделать с подсказками паролей, но всё-таки уже получилось хоть что-то адекватное.
Обратите внимание, что шифрование — не идеальное решение для хранения паролей. В связи с угрозами внедрения кода лучше избегать этого метода защиты. Для хранения паролей надежнее всего использовать алгоритм bcrypt. Но нельзя забывать и о том, что даже самые лучшие и проверенные решения обладают уязвимостями.
Сегодня оптимальным способом хэширования паролей считается использование bcrypt. Но многие разработчики всё ещё предпочитают старые и более слабые алгоритмы вроде MD5 и SHA-1. А некоторые при хэшировании даже не пользуются солью. В PHP 5.5 был представлен новый API для хэширования, который не только поощряет применение bcrypt, но и существенно облегчает работу с ним. Давайте разберём основы использования этого нового API.
- password_hash() — хэширование пароля;
- password_verify() — сравнение пароля с хэшем;
- password_needs_rehash() — перехэширование пароля;
- password_get_info() — возвращение названия алгоритма хэширования и применявшихся в ходе хэширования опций.
Несмотря на высокий уровень безопасности, обеспечиваемый функцией crypt(), многие считают её слишком сложной, из-за чего программисты часто допускают ошибки. Вместо неё некоторые разработчики используют для генерирования хэшей комбинации слабых алгоритмов и слабых солей:
Функция password_hash() существенно облегчает разработчику жизнь и повышает безопасность кода. Для хэширования пароля достаточно скормить его функции, и она вернёт хэш, который можно поместить в БД:
И всё! Первый аргумент — пароль в виде строки, второй аргумент задаёт алгоритм генерирования хэша. По умолчанию используется bcrypt, но при необходимости можно добавить и более сильный алгоритм, который позволит генерировать строки большей длины. Если в своём проекте вы используете PASSWORD_DEFAULT, то удостоверьтесь, что ширина колонки для хранения хэшей не менее 60 символов. Лучше сразу задать 255 знаков. В качестве второго аргумента можно использовать PASSWORD_BCRYPT. В этом случае хэш всегда будет длиной в 60 символов.
Обратите внимание, что вам не нужно задавать значение соли или стоимостного параметра. Новый API всё сделает за вас. Поскольку соль является частью хэша, то вам не придётся хранить её отдельно. Если вам всё-таки нужно задать своё значение соли (или стоимости), то это можно сделать с помощью третьего аргумента:
Всё это позволит вам использовать самые свежие средства обеспечения безопасности. Если в дальнейшем в PHP появится более сильный алгоритм хэширования, то ваш код станет использовать его автоматически.
Теперь рассмотрим функцию сравнения пароля с хэшем. Первый вводится пользователем, а второй мы берём из БД. Пароль и хэш используются в качестве двух аргументов функции password_verify(). Если хэш соответствует паролю, то функция возвращает true.
Помните, что соль является частью хэша, поэтому она не задаётся здесь отдельно.
Если вы захотите повысить уровень безопасности, добавив более сильную соль или увеличив стоимостный параметр, или изменится алгоритм хэширования по умолчанию, то вы, вероятно, захотите перехэшировать все имеющиеся пароли. Данная функция поможет проверить каждый хэш на предмет того, какой алгоритм и параметры использовались при его создании:
Не забывайте, что вам нужно будет это делать в тот момент, когда пользователь пытается залогиниться, поскольку это единственный раз, когда у вас будет доступ к паролю в виде простого текста.
- algo — константа, позволяющая идентифицировать алгоритм;
- algoName — название использовавшегося алгоритма;
- options — значения разных опций, применявшихся при хэшировании.
Как видите, работать с новым API не в пример легче, чем с неуклюжей функцией crypt(). Если же вы используете более ранние версии PHP, то рекомендую обратить внимание на библиотеку password_compact. Она эмулирует данный API и автоматически отключается, когда вы обновляетесь до версии 5.5.
К сожалению, до сих пор не существует идеального решения для защиты данных. К тому же всегда есть риск взлома вашей системы безопасности. Однако борьба снаряда и брони не прекращается. Например, наш арсенал средств защиты относительно недавно пополнился так называемыми функциями губки.
Здесь старая версия урока, которая больше не обновляется.
Итак, ты решил сделать авторизацию и регистрацию через пароли. Как максимально обезопасить пароли пользователей от взлома и от своих же любопытных сотрудников (если ты работаешь не один, а в большой компании)?
Для начала, никогда не храни открытые пароли. Храни соленые хеши от них. Хеш-функция, например md5, sha1 (про них написано в вики, почитай) — это практически необратимая функция. То есть получить хеш по паролю просто, а вот восстановить пароль, имея хеш практически невозможно — надо перебирать все возможные варианты паролей и сравнивать получившиеся хеши.
Какой смысл в хэше, если md5 все равно можно расшифровать? Пусть даже перебором?
Это займет много или очень много времени. Может, взломщик устанет ждать или пароли потеряют актуальность. Например, если хорошо шифровать, то годы (по идее там перебирать можно и 100 лет, но я думаю скоро изобретут какую-нибудь штуку для ускоренного перебора), вместо того чтобы взять и увидеть пароли в открытую.
Ок, достаточно ли использовать хеширование и хранить только хеши?
Нет! Без так называемой «соли» многие пароли можно подобрать за секунду если там использовать просто md5(pass). Не веришь? Читай ниже.
Что такое соль? Что значит «соленый хеш»?
Соль — случайно сгенерированная последовательность символов, которая хранится рядом с хешем. Дан пароль $pass = "123456" , мы генерируем случайную соль например $salt = 'A&%6t*(k:' и получаем хеш от «соль + пароль»: $hash = md5($salt . $pass) . В базу сохраняется отдельно использованная соль (она для каждого пользователя своя), отдельно хеш.
Теперь попробуем применить математику и посчитать насколько надежны разные способы хеширования. Сейчас подбор пароля делается 2 способами:
Заметь, что если у тебя база с кучей хешей, то их все можно проверять их все одновременно примерно с такой же скоростью как и один хеш.
Считаем число вариантов.
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-символьные пароли, у многих там просто слово или цифры.
Заметь что в будущем компьютеры будут мощнее, и значит подбираться пароли будут быстрее. Теперь подумаем как защититься и усложнить жизнь взломщикам:
Безопасность веб-приложений: Что можно, а что нельзя делать при шифровании с использованием соли.
Безопасность веб-приложений: Что можно, а что нельзя делать при шифровании с использованием соли
Обзор:
Вопрос безопасности баз данных стал более насущным по мере того, как базы данных становились более открытыми. Шифрование является одним из пяти основных факторов безопасности данных.
Небезопасной практикой является хранение такой важной информации, как пароль, номер кредитной карты в базе данных в незашифрованном виде. Эта статья охватывает различные возможности шифрования.
Даже в если вы зашифровали вашу информацию, это совершенно не значит, что она находится в полной безопасности. В этой статье рассматриваются действия со стороны злоумышленника.
Приложения, не использующие хеши:
Во время изучения веб-приложения я дошел до этого места программы. Программа использовала javascript для шифрования пароля пользователя перед отправкой. В качестве соли использовался текущий ID сессии.
Итак, на сервере:
На сервере программа не сможет проверить значение пароля по причине использования соли и случайного ID сессии. И поскольку MD5 является нереверсивной хеш-функцией, пароль не сможет быть проверен до тех пор, пока пароли хранятся в виде текста в базе данных.
В качестве соли для шифрования пароля перед отправкой используется случайно сгенерированный ID сессии. Это значит, что серверные базы данных не будут зашифрованы.
Иногда такого рода программы выдают много информации.
Пункт №1: Всегда шифруйте вашу базу данных паролей.
- Алгоритм, используемый для хеширования, должен иметь какие-то изъяны. Хеши должны быть реверсивными
- Использование брут-форс атаки для перебора хешей с помощью словаря или радужных таблиц.
- Или у вас просто UPDATE привилегии. Тогда просто замените значения хешей паролей на известные вам.
Для того, чтобы использовать все эти виды атак, вы должны знать, при помощи какого алгоритма был посчитан хеш.
Что можно сделать, чтобы выяснить используемый алгоритм хеширования??
Ответ: Все алгоритмы генерируют хеш фиксированной длины. Поэтому на основании выходного значения вы можете прикинуть, какой алгоритм использовался☺. “Все это – достаточно известные факты”, но по-прежнему я помещаю их здесь.
Для этого я размещу небольшую таблицу для выявления хеш-функций на основе их выходного значения
Функция: md5(“входные данные”); Hash(“входные данные”); Вывод: 32 Символа Пример: “5f4dcc3b5aa765d61d8327d eb882cf99”
Функция: System.Security.Cryptogr aphy Вывод: 32 Символа Пример: “5f4dcc3b5aa765d61d8327d eb882cf99”
Функция:java.secur ity.MessageDigest Вывод: 32 Символа Пример: “5f4dcc3b5aa765d61d 8327deb882cf99”
Функция: Crypt() По-умолчанию DES вывод: 13 Символов Пример: “sih2hDu1acVcA”
Читайте также: