Основы хеширования паролей PHP


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



Абсолютно случайный

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

Безопасность криптографии зависит от случайности. “Криптографически безопасный” алгоритм - это алгоритм, который делает свой вывод полностью неотличимым от случайного шума. Этого трудно достичь на практике, но для разработчиков PHP доступны удобные расширения и методы, которые решат эту проблему за вас.

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

Многие старые учебные пособия ссылаются на функцию mt_rand() как генератор псевдослучайных чисел для использования в PHP. Он действительно выдает псевдослучайные числа, но функция не является криптографически безопасной. Если вы знаете начальное число, используемое для начала последовательности, вы можете легко предсказать каждое число, которое выдает функция. Это напоминание о том, что вы должны полагаться только на криптографически защищенные функции для операций безопасности в PHP.

Факторы времени и памяти

На момент написания этой статьи для использования с password_hash() доступны три алгоритма. Алгоритм по умолчанию сегодня CRYPT_BLOWFISH и может быть использован путем передачи либо PASSWORD_DEFAULT, либо PASSWORD_BCRYPT в качестве флага для метода:

$hash = password_hash($password, PASSWORD_DEFAULT);
В качестве альтернативы, если PHP был скомпилирован с поддержкой Argon2, вы можете передать PASSWORD_ARGON2I или PASSWORD_ARGON2ID, чтобы использовать алгоритмы Argon2i или Argon2id соответственно. Каждый выбор алгоритма предлагает вам различные преимущества в плане безопасности, но каждый алгоритм будет иметь идентификатор, закодированный в хэш-выводе функции. Даже если алгоритм по умолчанию в PHP изменится в будущем, когда вы использовали PASSWORD_DEFAULT в прошлом, система может обнаружить использование bcrypt и правильно проверить хэш. Функция password_needs_rehash() также может указывать на то, что вам следует обновить и повторно хешировать пароль, если вы измените алгоритм хеширования в будущем.

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

Медленный хэш не является проблемой для повседневной аутентификации пользователя. Если хэширование пароля занимает одну секунду, то для аутентификации пользователя с правильным паролем потребуется одна секунда. Предполагая, что злоумышленник попытается угадать пароль методом перебора, он сможет угадывать только один пароль в секунду. Учитывая шесть буквенно-цифровых символов, существует 56 800 235 584 возможных пароля. Опять же, угадывая один пароль в секунду, потребовалось бы более 1800 лет, чтобы перепробовать все возможные комбинации паролей.

Алгоритм PASSWORD_BCRYPT поддерживает коэффициент стоимости — по умолчанию он использует стоимость 10. Чем выше стоимость, тем сложнее вашей машине придется работать, чтобы сгенерировать хэш. Учитывая, что мы хотим настроить таргетинг на один хэш в секунду, мы можем рассчитать соответствующий коэффициент затрат для данного сервера, используя процедуру в листинге 1.

$cost = 8;

do {
  $cost += 1;
  $start = microtime(true);
  password_hash('test', PASSWORD_BCRYPT,
                ['cost' => $cost]);
  $end = microtime(true);
} while (($end - $start) < 1);

echo sprintf('Time cost => %d', $cost);
При запуске на одном моем сервере с такими параметрами: PHP 7.4, Ubuntu 16.04 64bit, 512 МБ RAM и 1 CPU коэффициент получился 14. Эта стоимость намного выше, чем значение по умолчанию 10.

Семейство хэшей Argon2 использует аналогичные настройки, но позволяет указывать как временные затраты, так и затраты памяти. В этом примере мы настроим только стоимость времени, начиная с значения по умолчанию 2 (Листинг 2).

$cost = 2;

do {
  $cost += 1;
  $start = microtime(true);
  password_hash('test', PASSWORD_ARGON2I,
                ['time_cost' => $cost]);
  $end = microtime(true);
} while (($end - $start) < 1);

echo sprintf('Time cost => %d', $cost);
Сервер, упомянутый выше предлагает временные затраты в размере 11. Чтобы контролировать скорость вашего хеширования, ваша команда должна проверить фактическую скорость хеширования вашего серверного оборудования и соответствующим образом настроить процесс!

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

Безопасная проверка пароля

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

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

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

$valid = password_verify($password, $stored_hash);
if (!$valid) {
  throw new UnauthorizedException('Invalid password!');
}
PHP будет использовать информацию в сохраненном хэше (т.е. алгоритм и факторы стоимости) и запускать предоставленный открытый текст с помощью той же операции хеширования, которую он использовал ранее. Если вывод хэша совпадает с известным значением хэша, функция возвращает значение true. В противном случае он возвращает false, и разработчику остается справиться с этим отрицательным возвратом.

В заключение

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

Не тратьте время на попытки написать свой собственный алгоритм хеширования. Не пытайтесь изобретать свои собственные протоколы защиты паролем или проверки. Не доверяйте онлайн-учебникам, которые используют какие-либо другие функции хеширования паролей. Используйте встроенные функции хеширования и проверки паролей, предоставляемые PHP. Он эффективен и может быть настроен таким образом, чтобы обеспечить максимальную защиту ваших пользователей.
Автор этого материала - я - Пахолков Юрий. Я оказываю услуги по написанию программ на языках Java, C++, C# (а также консультирую по ним) и созданию сайтов. Работаю с сайтами на CMS OpenCart, WordPress, ModX и самописными. Кроме этого, работаю напрямую с JavaScript, PHP, CSS, HTML - то есть могу доработать ваш сайт или помочь с веб-программированием. Пишите сюда.



тегизаметки, php, хэширование, информационная безопасность, пароли




Отправляя сообщение я подтверждаю, что ознакомлен и согласен с политикой конфиденциальности данного сайта.




Замена задачи с сохранением конечного результата
Движение круга в замкнутой плоскости на флеш as3
Идентичность и равенство объектов в Java