Как сделать хеширование пароля php

Обновлено: 03.07.2024

Хранить пароли пользователей в открытом виде в БД — не самый лучший вариант, особенно когда к этой базе данных могут получить доступ другие. И, правда, при взломе базы пароли пользователей, хранящиеся в открытом виде, будут банально украдены, а это уже чревато серьёзными репутационными и финансовыми потерями, чего не может допустить никакой системный администратор. В этой статье будет рассмотрен процесс хэширования паролей (hashing) — процесс несложный, но, к сожалению, порой игнорируемый веб-программистами.

Да, хэширование панацеей не является, но всё же при краже данных оно может существенно снизить ущерб. Ведь что собой представляет хэш-функция (hash, хэш, хеш)? Речь идёт о функции, которая обладает бесконечной областью определения, имея при этом конечную область значения. А ещё функции типа hash присуща одна интересная особенность: даже в случае небольшого изменения входного значения у хэш-функции, итоговое значение изменится просто радикально. Если говорить языком криптографию, стоит отметить главное назначение хэш-функций — генерация ключей на основе запоминаемых и коротких паролей (лично вам, легко ли было бы запомнить 16 шестнадцатеричных разрядов?) То-то же.

Подытожив первую часть статьи, скажем, что hash генерирует хэш-код.

Описание hash в PHP

Описать хэш (хеш) в PHP можно следующим образом:

Возвращаемые значения

При работе функции hash выполняется возврат строки, содержащей вычисленный код в нижнем регистре и в шестнадцатеричной кодировке. Но если raw_output будет задан как TRUE, произойдёт возврат кода в виде бинарных данных.

PHP хэш: хэширование MD5

Простейший пример практического применения хэша MD5 — шифрование пользовательских паролей. Как уже было сказано, хранить пароли в БД в открытом виде не просто моветон, а в принципе недопустимо. И вот здесь как раз и может прийти на помощь функция хэширования в PHP и соответствующий алгоритм.

В языке программирования PHP функция хэширования MD5 называется md5() . Она принимает одну строку, которую надо зашифровать. Возвращает же MD5-хэш (hash). На практике это может выглядеть следующим образом:

Если вы запустите этот скрипт, увидите MD5-хэш, который будет соответствовать строке "Password".

Теперь простейший скрипт для проверки пароля и логина:

Свойства MD5

У вышерассмотренного алгоритма есть следующие свойства: • hash содержит 32 символа; • hash уникален для каждой строки; • сам процесс MD5-хэширования является необратимым; • этот процесс отличается достаточной медлительностью.

Что касается третьего пункта, то он весьма важен, ведь какой был бы смысл в хэшировании, если бы оно было обратимым? И, действительно, не глупо ли шифровать пасворды, которые потом легко и просто можно расшифровать? Вопрос риторический.

Но если мы пойдём дальше, то увидим, что пункт номер 4 в нашем списке — это тоже плюс, как бы это не звучало парадоксально. Дело в том, что если человек, набирающий пароль, вынужден подождать 0,001 секунды (именно столько и длится процесс), то для него это не проблема — он от этого не пострадает и даже не заметит этой самой медлительности. Если же мы говорим про хакера, которому надо перебрать множество вариантов, то медлительность алгоритма придётся ему очень не кстати, ведь алгоритм задаёт предел — 1000 паролей в секунду. Да, многое зависит и от быстродействия сервера, но несмотря на это, низкая скорость при получении хеша MD5 — это всё-таки проблема для злоумышленника.

На этом закончим и в очередной раз напомним: никогда не храните ваши пароли и пароли ваших пользователей в открытом виде — просто забудьте, что так можно делать, даже (и тем более) если вы обычный веб-мастер. Всегда отправляйте ваши пароли в БД в виде hash (алгоритм выбирайте на своё усмотрение) и сравнивайте не сами пасворды, а их хэши.

В этом разделе разъясняются причины, стоящие за хешированием паролей в целях безопасности, а также эффективные методы хеширования.

Почему я должен хешировать пароли пользователей в моем приложении?

Хеширование паролей является одним из самых основных соображений безопасности, которые необходимо сделать, при разработке приложения, принимающего пароли от пользователей. Без хеширования, пароли, хранящиеся в базе вашего приложения, могут быть украдены, например, если ваша база данных была скомпрометирована, а затем немедленно могут быть применены для компрометации не только вашего приложения, но и аккаунтов ваших пользователей на других сервисах, если они не используют уникальных паролей.

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

Важно заметить, однако, что хеширование паролей защищает их только от компрометирования в вашем хранилище, но не обязательно от вмешательства вредоносного кода в вашем приложении.

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

Использование BCrypt является общепринятым и лучшим способом для хэширования паролей, но большое количество разработчиков по-прежнему используют старые и слабые алгоритмы, вроде MD5 и SHA1. Некоторые разработчики даже не используют соль для хэширования. Новый API хэширования в PHP 5.5 ставит своей целью привлечь внимание к BCrypt, упрощая работу с ним. В этой статье я расскажу об основах использования нового API для хеширования в PHP.

  • password_hash() — используется для хэширования пароля.
  • password_verify() — используется для проверки пароля на соответствие хэшу.
  • password_needs_rehash() — используется для проверки необходимости создать новый хэш.
  • password_get_info() — возвращает имя алгоритма хеширования и различные параметры, используемые при хэшировании.

password_hash()

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

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

Вот и все! Первым параметром является строка пароля, который необходимо захэшировать, а второй параметр определяет алгоритм, который должен быть использован для генерирования хэша.

Алгоритм по умолчанию, в настоящее время, BCrypt, но более сильный алгоритм может быть установлен по умолчанию, когда-нибудь в будущем, и, возможно, он будет генерировать большие строки. Если вы используете PASSWORD_DEFAULT в ваших проектах, обязательно храните хэш в колонке, размером больше 60 символов. Установка размера колонки до 255 может быть хорошим выбором. Вы также можете использовать PASSWORD_BCRYPT в качестве второго параметра. В этом случае результат всегда будет 60 символов.

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

Таким образом, Вы будете всегда идти в ногу с актуальными мерами безопасности. Если PHP позже примет решение о применении более мощного алгоритма хеширования, ваш код может воспользоваться им без изменений.

password_verify()

Теперь, когда вы видели, как генерировать хэши с новым API, давайте посмотрим, как проверить пароль. Мы просто берем хэш из базы, и пароль, введенный пользователем и передаем их в эту функцию. password_verify() возвращает true, если хэш соответствует указанному паролю.

Соль является частью хэша и именно поэтому нам не придется возиться с ней отдельно.

password_needs_rehash()

Что делать, если вам нужно изменить соль или стоимость вычисления для сохраненных паролей? Например, вы решили усилить безопасность и увеличить стоимость вычисления или изменить соль. Или PHP изменил алгоритм хэширования, используемый по умолчанию. В этих случаях вы хотели бы изменить существующие хэши паролей. Функция password_needs_rehash() проверяет, использует ли хэш пароля конкретный алгоритм, соль и стоимость вычисления.

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

password_get_info()

  • algo — константа, которая идентифицирует конкретный алгоритм
  • algoName — название используемого алгоритма
  • options — различные опции, используемые при генерации хэша

Заключение

Новый API хэширования паролей, безусловно, удобнее, чем возня с crypt(). Если ваш сайт в настоящее время работает на PHP 5.5, то я настоятельно рекомендуется использовать новое API хэширования. Те, кто используют PHP 5.3.7 (или более новой версии), могут использовать библиотеку под названием password_compat которая эмулирует это API и автоматически отключает себя при использовании PHP версии 5.5+.

Хранить пароль в открытом виде - неправильно. Хакер-злоумышленник может получить доступ к вашей базе данных и украсть пароли.

Поэтому обычно логин хранится в открытом виде, а пароль хешируется специальной функцией md5 , которая параметром принимает пароль, а возвращает его , по которому нельзя восстановить этот самый пароль.

Давайте, например, найдем хеш какой-нибудь строки:

<?php echo md5('12345'); //выведет '827ccb0eea8a706c4c34a16891f84e7b' ?>

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

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

Описанная правка будет представлять собой что-то такое:

<?php $login = $_POST['login']; $password = md5($_POST['password']); // преобразуем пароль в его хеш $query = "INSERT INTO users SET login='$login', password='$password'"; ?>

Внесем аналогичные правки в авторизацию:

<?php $login = $_POST['login']; $password = md5($_POST['password']); // преобразуем пароль в его хеш $query = "SELECT * FROM users WHERE login='$login' AND password='$password'"; ?>

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

Добавляем соль в регистрацию

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

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

Речь идет о достаточно простых, популярных паролях.

Загуглите, например, хеш 827ccb0eea8a706c4c34a16891f84e7b и сразу в поиске гугла вы увидите, что это пароль '12345' .

Хеши достаточно сложных паролей таким образом не разгадать (попробуйте).

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

Мы можем при регистрации заставлять придумывать более длинные пароли, ограничивая, к примеру, минимальное количество символов 6 -ю или 8 -ю, однако, все равно будут появляться пароли вида '123456' или '12345678' .

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

Суть этого решения такая: пароли надо . Соль - это специальная случайная строка, которая будет добавляться к паролю при регистрации и хеш уже будет вычисляться не от простого пароля типа, а от строки соль+пароль, то есть от соленого пароля.

То есть при регистрации вы будете делать что-то типа такого:

<?php $salt = '1sJg3hfdf'; // соль - сложная случайная строка $password = md5($salt . $_POST['password']); // преобразуем пароль в соленый хеш ?>

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

Вот готовая функция, которая сделает это:

С помощью этой функции можно переписать наш код вот так:

<?php $salt = generateSalt(); // соль $password = md5($salt . $_POST['password']); // соленый пароль ?>

Еще раз повторю, что это были изменения при регистрации - в БД сохраняем не просто хеш пароля, а хеш соленого пароля.

Это еще не все: в таблице с юзерами кроме поля login и password нужно сделать еще и поле salt , в котором мы будем хранить соль каждого пользователя.

Реализуйте описанную выше регистрацию с соленым паролем.

Добавляем соль в авторизацию

Теперь нам необходимо поменять авторизацию. Здесь уже изменения будут более существенными.

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

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

Учтите, что может такое быть, что логин вбит неправильно, в этом случае проверку пароля можно не осуществлять, а сразу вывести, что авторизация не возможна - данные не верны:

Давайте добавим проверку пароля:

Реализуйте описанную выше авторизацию с соленым паролем. Попробуйте зарегистрируйтесь, авторизуйтесь, убедитесь, что все работает.

Используем функцию password_hash

На самом деле функция md5 и соление пароля с ее помощью считается устаревшим. Мы изучали ее, чтобы вы поняли дальнейший материал, а также потому, что вы можете столкнуться с этим, работая с чужими проектами.

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

Попробуйте несколько раз запустите этот код:

Вы каждый раз будете получать разный результат и в этом результате первая часть строки будет являться солью, а вторая часть - соленым паролем.

Пусть у нас есть хеш, полученный из функции password_hash и какой-то пароль. Чтобы проверить, это хеш этого пароля или нет, следует использовать функцию password_verify - первым параметром она принимает пароль, а вторым - хеш, и возвращает true или false .

Давайте посмотрим на примере:

<?php $password = '12345'; // пароль $hash = '$2y$10$xoYFX1mFPxBSyxaRe3iIRutxkIWhxGShzEhjYUVd3qpCUKfJE1k7a'; // хеш if (password_verify($password, $hash)) < // хеш от этого пароля >else < // хеш не от этого пароля >?>

Что это дает нам на практике: мы можем не создавать в базе данных отдельное поле для хранения соли, не заморачиваться с генерированием этой соли - PHP все сделает за нас!

Теперь давайте поправим код регистрации. Вот то, что есть сейчас:

<?php function generateSalt() < $salt = ''; $saltLength = 8; // длина соли for($i = 0; $i < $saltLength; $i++) < $salt .= chr(mt_rand(33, 126)); // символ из ASCII-table >return $salt; > $salt = generateSalt(); // соль $password = md5($salt . $_POST['password']); // преобразуем пароль в соленый хеш ?>

С помощью password_hash мы сократим это до:

<?php $password = password_hash($_POST['password'], PASSWORD_DEFAULT); ?>

Аналогичным образом подправится код авторизации:

Переделайте вашу авторизацию и регистрацию на новые изученные функции.

password_hash (при первом чтении можно пропустить). В нем мы указываем алгоритм шифрования. На самом деле, этот параметр зарезервирован создателями PHP на будущее.

В настоящее время алгоритмом по умолчанию будет алгоритм BCrypt (то есть аналог md5, но мощнее), который своим результатом возвращает строку соль+хеш размером 60 символов.

Это значит, что поле password в базе данных следует установить такого размера - 60 символов, иначе ничего не будет работать верно.

Алгоритм BCrypt можно указать явно во втором параметре функции password_hash , написав там PASSWORD_BCRYPT .

Если же оставить там PASSWORD_DEFAULT - то PHP возьмет алгоритм по умолчанию, то есть тот же BCrypt - но это в настоящее время. В будущем возможно BCrypt устареет также, как и md5 и будет заменен более мощным алгоритмом.

В таком случае изменения в вашем коде произойдут автоматически при переходе на новую версию PHP.

Главная причина - минимизация ущерба в случае утечки базы данных.

Если злоумышленник получит только логины и email-адреса пользователей - это плохо, но не критично.

В тот же личный кабинет Пятёрочки, чтобы перевыпустить карту и потратить чужие бонусы.

Мем с бабкой - Где мои баллы?!

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

Некоторые разработчики считают, что их приложение надёжно защищено и никаких утечек быть не может. Есть несколько причин, почему это мнение ошибочно:

  • Разработчик - не робот, он не может не совершать ошибок.
  • Взлом может произойти со стороны хостинг-провайдера, работу которого не всегда возможно контролировать.
  • Некорректная настройка сервера может привести к возможному доступу других пользователей хостинга к вашему сайту (актуально для виртуальных хостингов).
  • Бывший коллега по работе может слить базу данных конкурентам. Может в качестве мести, а может просто ради денег.

Короче, пароли в открытом виде хранить нельзя.

Шифрование и хеширование

Шифрование - это обратимое преобразование текста в случайный набор символов.

Хеширование - это необратимое преобразование текста в случайный набор символов.

Разница между этими двумя действиями в том, можем ли мы из случайного набора символов получить исходную строку по какому-то известному алгоритму.

Зашифровали. Теперь для расшифровки нужно выполнить обратную операцию, сдвинуть все буквы на 1 символ назад. К слову, этот алгоритм шифрования называется шифр Цезаря (Википедия).

В отличие от шифрования, хеширование не имеет (вернее, не должно иметь) способа "расхешировать" строку обратно:

Шифрование паролей

Не надо шифровать пароли.

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

Хеширование паролей и авторизация

Для хеширования паролей в PHP существует функция password_hash() :

Вторым параметром передаётся алгоритм хеширования. По-умолчанию это указанный нами bcrypt, но я рекомендую указывать его вручную, поскольку базовый алгоритм в будущем может поменяться. Будет грустно, если при очередном обновлении версии PHP на сайте отвалится авторизация.

Для проверки корректности введённого пользователем пароля используется функция password_verify() :

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

При попытке авторизации получаем пользователя по его логину и проверяем функцией password_verify() , соответствует ли хеш пароля тому паролю, который ввёл пользователь.

Таким образом, хранить исходный пароль больше нет смысла.

Да, разные алгоритмы хеширования генерируют хеш разной длины, поэтому рекомендуется хранить хеш в поле с типом VARCHAR(255).

Алгоритмы MD5 и SHA1

В интернете ещё встречаются статьи, где рекомендуется хешировать пароли функциями md5() и sha1() .

Для хеширования паролей их использовать нельзя!

Эти алгоритмы давно устарели и не являются безопасными (Документация). Вместо этого используйте функцию password_hash() , которую мы разобрали выше.

Читайте также: