Как расхешировать пароль php
Перейти к содержимому

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

  • автор:

password_hash

password_hash() создаёт хеш пароля используя сильный, необратимый алгоритм хеширования.

В данный момент поддерживаются следующие алгоритмы:

  • PASSWORD_DEFAULT — используется алгоритм bcrypt (по умолчанию с PHP 5.5.0). Обратите внимание, что используемый алгоритм может со временем меняться на более сильный, когда таковой добавляется в PHP. Соответственно и длина результата может со временем меняться. В связи с этим рекомендуется выбирать длину поля для хранения в базе данных более 60 символов (255 символов могло быть хорошим вариантом).
  • PASSWORD_BCRYPT — использует алгоритм CRYPT_BLOWFISH . Генерирует стандартный хеш, совместимый с генерированным функцией crypt() с использованием идентификатора "$2y$". В результате будет сгенерирована строка длиной 60 символов или false в случае возникновения ошибки.
  • PASSWORD_ARGON2I — Использовать алгоритм хеширования Argon2i. Этот алгоритм доступен только если PHP собран с поддержкой Argon2.
  • PASSWORD_ARGON2ID — Использовать алгоритм хеширования Argon2id. Этот алгоритм доступен только если PHP собран с поддержкой Argon2.

Поддерживаемые опции для PASSWORD_BCRYPT :

salt ( string ) — для самостоятельного задания соли для хеширования. Обратите внимание, что это приведёт к переопределению и предотвращению автоматического создания соли.

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

Эта опция объявлена устаревшей. Рекомендуется использовать автоматически генерируемую соль. Начиная с PHP 8.0.0 явно заданная соль игнорируется.

cost ( int ) — задаёт необходимую алгоритмическую сложность. Пример использования этого значения можно посмотреть на странице посвящённой функции crypt() .

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

Поддерживаемые опции для PASSWORD_ARGON2I и PASSWORD_ARGON2ID :

memory_cost ( int ) — Максимальный размер памяти (в килобайтах), которую можно использовать для вычисления хеша Argon2. По умолчанию PASSWORD_ARGON2_DEFAULT_MEMORY_COST .

time_cost ( int ) — Максимально возможное время, которое можно потратить для вычисления хеша Argon2. По умолчанию PASSWORD_ARGON2_DEFAULT_TIME_COST .

threads ( int ) — Количество потоков, которые можно использовать для вычисления хеша Argon2. По умолчанию PASSWORD_ARGON2_DEFAULT_THREADS .

Доступно только тогда, когда PHP использует libargon2, но не при реализации libsodium.

Список параметров

Использование алгоритма PASSWORD_BCRYPT приведёт к обрезанию поля password до максимальной длины 72 байта.

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

Ассоциативный массив с опциями. За документацией по поддерживаемым опциям для каждого алгоритма обратитесь к разделу Константы алгоритмов хеширования паролей.

Если не задано, то будет использована стандартная стоимость и соль будет генерироваться автоматически.

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

Возвращает хешированный пароль.

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

Список изменений

Версия Описание
8.0.0 password_hash() больше не возвращает значение false в случае возникновения ошибки, вместо этого будет выброшено исключение ValueError , если алгоритм хеширования пароля недействителен или Error , если хеширование пароля не удалось из-за неизвестной ошибки.
8.0.0 Параметр algo теперь допускает значение null .
7.4.0 Параметр algo сейчас ожидает строку ( string ), но всё ещё принимает число ( int ) для обратной совместимости.
7.4.0 Модуль sodium обеспечивает альтернативную реализацию паролей Argon2.
7.3.0 Добавлена поддержка алгоритма хеширования паролей Argon2id с помощью PASSWORD_ARGON2ID .
7.2.0 Добавлена поддержка хеширующего алгоритма Argon2i с помощью PASSWORD_ARGON2I .

Примеры

Пример #1 Пример использования password_hash()

Результатом выполнения данного примера будет что-то подобное:

Пример #2 Пример использования password_hash() с ручным заданием стоимости

Результатом выполнения данного примера будет что-то подобное:

Пример #3 Пример поиска хорошего значения стоимости для password_hash()

<?php
/**
* Данный код замерит скорость выполнения операции хеширования для вашего сервера
* с разными значениями алгоритмической сложности для определения максимального
* его значения, не приводящего к деградации производительности. Хорошее базовое
* значение лежит в диапазоне 8-10, но если ваш сервер достаточно мощный, то можно
* задать и больше. Данный скрипт ищет максимальное значение, при котором
* хеширование уложится в 50 миллисекунд.
*/
$timeTarget = 0.05 ; // 50 миллисекунд.

$cost = 8 ;
do <
$cost ++;
$start = microtime ( true );
password_hash ( «test» , PASSWORD_BCRYPT , [ «cost» => $cost ]);
$end = microtime ( true );
> while (( $end — $start ) < $timeTarget );

echo «Оптимальная стоимость: » . $cost ;
?>

Результатом выполнения данного примера будет что-то подобное:

Пример #4 Пример использования password_hash() с Argon2i

Результатом выполнения данного примера будет что-то подобное:

Примечания

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

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

Замечание:

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

  • Любой новый алгоритм должен присутствовать в ядре как минимум 1 полный релиз PHP для того, чтобы его можно было установить по умолчанию. Таким образом, если, к примеру, новый алгоритм был добавлен в 7.5.5, то задать по умолчанию его можно будет только в 7.7 (7.6 будет тем самым полным релизом, в течение которого он должен присутствовать, от 7.6.0 до 7.7.0). Но если новый алгоритм добавлен в 7.6.0, то его также можно будет задать по умолчанию в 7.7.0.
  • Алгоритм по умолчанию может быть изменён только в полном релизе (7.3.0, 8.0.0, и т.д.), но не в промежуточных. Единственное исключение — это если в текущем алгоритме найдена критическая уязвимость.

Смотрите также

  • password_verify() — Проверяет, соответствует ли пароль хешу
  • password_needs_rehash() — Проверяет, что указанный хеш соответствует заданным опциям
  • crypt() — Необратимое хеширование строки
  • sodium_crypto_pwhash_str() — Получить ASCII-кодированный хеш

User Contributed Notes 12 notes

Since 2017, NIST recommends using a secret input when hashing memorized secrets such as passwords. By mixing in a secret input (commonly called a «pepper»), one prevents an attacker from brute-forcing the password hashes altogether, even if they have the hash and salt. For example, an SQL injection typically affects only the database, not files on disk, so a pepper stored in a config file would still be out of reach for the attacker. A pepper must be randomly generated once and can be the same for all users. Many password leaks could have been made completely useless if site owners had done this.

Since there is no pepper parameter for password_hash (even though Argon2 has a «secret» parameter, PHP does not allow to set it), the correct way to mix in a pepper is to use hash_hmac(). The «add note» rules of php.net say I can’t link external sites, so I can’t back any of this up with a link to NIST, Wikipedia, posts from the security stackexchange site that explain the reasoning, or anything. You’ll have to verify this manually. The code:

<?php
// register.php
$pepper = getConfigVariable ( «pepper» );
$pwd = $_POST [ ‘password’ ];
$pwd_peppered = hash_hmac ( «sha256» , $pwd , $pepper );
$pwd_hashed = password_hash ( $pwd_peppered , PASSWORD_ARGON2ID );
add_user_to_database ( $username , $pwd_hashed );
?>

<?php
// login.php
$pepper = getConfigVariable ( «pepper» );
$pwd = $_POST [ ‘password’ ];
$pwd_peppered = hash_hmac ( «sha256» , $pwd , $pepper );
$pwd_hashed = get_pwd_from_db ( $username );
if ( password_verify ( $pwd_peppered , $pwd_hashed )) <
echo «Password matches.» ;
>
else <
echo «Password incorrect.» ;
>
?>

Note that this code contains a timing attack that leaks whether the username exists. But my note was over the length limit so I had to cut this paragraph out.

Also note that the pepper is useless if leaked or if it can be cracked. Consider how it might be exposed, for example different methods of passing it to a docker container. Against cracking, use a long randomly generated value (like in the example above), and change the pepper when you do a new install with a clean user database. Changing the pepper for an existing database is the same as changing other hashing parameters: you can either wrap the old value in a new one and layer the hashing (more complex), you compute the new password hash whenever someone logs in (leaving old users at risk, so this might be okay depending on what the reason is that you’re upgrading).

Why does this work? Because an attacker does the following after stealing the database:

password_verify(«a», $stolen_hash)
password_verify(«b», $stolen_hash)
.
password_verify(«z», $stolen_hash)
password_verify(«aa», $stolen_hash)
etc.

(More realistically, they use a cracking dictionary, but in principle, the way to crack a password hash is by guessing. That’s why we use special algorithms: they are slower, so each verify() operation will be slower, so they can try much fewer passwords per hour of cracking.)

Now what if you used that pepper? Now they need to do this:

password_verify(hmac_sha256(«a», $secret), $stolen_hash)

Without that $secret (the pepper), they can’t do this computation. They would have to do:

password_verify(hmac_sha256(«a», «a»), $stolen_hash)
password_verify(hmac_sha256(«a», «b»), $stolen_hash)
.
etc., until they found the correct pepper.

If your pepper contains 128 bits of entropy, and so long as hmac-sha256 remains secure (even MD5 is technically secure for use in hmac: only its collision resistance is broken, but of course nobody would use MD5 because more and more flaws are found), this would take more energy than the sun outputs. In other words, it’s currently impossible to crack a pepper that strong, even given a known password and salt.

Хеширование пароля на PHP

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Использование password_hash для хеширования и проверки паролей в PHP

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

Насколько я понимаю, салт уже генерируется в строке, когда используется password_hash ?

Еще один вопрос, который у меня возник: не было бы разумно иметь два салта? Один прямо в файле, а другой — в БД? Таким образом, если кто-то скомпрометирует ваш салт в БД, он все равно останется прямо в файле?

Ответ 1

Использование password_hash — это рекомендуемый способ хранения паролей. Не разделяйте их на БД и файлы.

Допустим, у нас есть следующие данные:

$password = $_POST[‘password’];

Сначала вы хешируете пароль, выполнив следующие действия:

$hashed_password = password_hash($password, PASSWORD_DEFAULT);

Затем имеем результат:

var_dump($hashed_password);

Как видите, он хеширован.

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

// Запрос из базы данных логина и пароля

if(password_verify($password, $hashed_password)) <

// Если введенный пароль совпал с х е шированным паролем в базе данных

>

// Иначе переходим обратно на страницу входа в систему.

Ответ 2

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

// Хеширование нового пароля для хранения в базе данных.

// Функция автоматически генерирует криптографически безопасный салт.

$hashToStoreInDb = password_hash($_POST[‘password’], PASSWORD_DEFAULT);

// Проверяем, совпадает ли х е ш введенного пароля для входа в систему с хранящимся там х е шем.

// Салт и коэффициент будут извлечены из $existingHashFromDb.

$isPasswordCorrect = password_verify($_POST[‘password’], $existingHashFromDb);

Второй салт, который вы упомянули (тот, что хранится в файле), на самом деле является ключом на стороне сервера. Однако есть способ получше: вы можете сначала вычислить хеш, а затем зашифровать двусторонний хеш серверным ключом. Это дает вам возможность при необходимости менять ключ.

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

Ответ 3

Никогда не используйте функцию md5() для защиты пароля, даже с салтом, это всегда опасно!

Защитите свой пароль с помощью новейших алгоритмов хеширования, как показано ниже :

<?php

// оригинальный пароль

$password = ‘121@121’;

$password_encrypted = password_hash($password, PASSWORD_BCRYPT);

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

<?php

if (password_verify($password_inputted_by_user, $password_encrypted)) <

// Успешно!

echo ‘Пароль корректный’;

>else <

// Неверные учетные данные

echo ‘Неверный пароль’;

>

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

<?php

$options = [

‘salt’ => your_custom_function_for_salt(),

// собственный код для генерации подходящего и безопасного салта

‘cost’ => 12;

]

$hash = password_hash($your_password, PASSWORD_DEFAULT, $options);

Мы будем очень благодарны

если под понравившемся материалом Вы нажмёте одну из кнопок социальных сетей и поделитесь с друзьями.

How can I decrypt a password hash in PHP?

I need to decrypt a password. The password is encrypted with password_hash function.

Now, let’s assume that $crypted is stored in a database (there’s a «users» table, with usernames, passwords, etc) and I need to do a login: I have to see if the password entered by the user matches the encrypted password stored in the database.

This is the sql code.

. but $inputpassword is not encrypted, so it’s not equal to what is stored in the password field of the table users.

So, there’s a function to decrypt after the use of password_hash ? Or should I change my encrypt method? Or what else?

5 Answers 5

Bcrypt is a one-way hashing algorithm, you can’t decrypt hashes. Use password_verify to check whether a password matches the stored hash:

In your case, run the SQL query using only the username:

And do the password validation in PHP using a code that is similar to the example above.

The way you are constructing the query is very dangerous. If you don’t parameterize the input properly, the code will be vulnerable to SQL injection attacks. See this Stack Overflow answer on how to prevent SQL injection.

Dharman's user avatar

The passwords cannot be decrypted as will makes a vulnerability for users. So, you can simply use password_verify() method to compare the passwords.

where, $upass is password entered by user and $userRow[‘user_pass’] is user_pass field in database which is encrypted by password_hash() function.

Stephen Rauch's user avatar

I need to decrypt a password. The password is crypted with password_hash function.

Its not clear to me if you need password_verify , or you are trying to gain unauthorized access to the application or database. Other have talked about password_verify , so here’s how you could gain unauthorized access. Its what bad guys often do when they try to gain access to a system.

First, create a list of plain text passwords. A plain text list can be found in a number of places due to the massive data breaches from companies like Adobe. Sort the list and then take the top 10,000 or 100,000 or so.

Second, create a list of digested passwords. Simply encrypt or hash the password. Based on your code above, it does not look like a salt is being used (or its a fixed salt). This makes the attack very easy.

Third, for each digested password in the list, perform a select in an attempt to find a user who is using the password:

So, rather than picking a user and trying to reverse their password, the bad guy picks a common password and tries to find a user who is using it. Odds are on the bad guy’s side.

Because the bad guy does these things, it would behove you to not let users choose common passwords. In this case, take a look at ProCheck, EnFilter or Hyppocrates (et al). They are filtering libraries that reject bad passwords. ProCheck achieves very high compression, and can digest multi-million word password lists into a 30KB data file.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *