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

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

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

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

Что такое хеширование паролей

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

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

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

к меню ↑

Как происходит хеширование

Существуют различные алгоритмы для создания хеш текста. Самыми популярными из них являются: MD5, SHA1, и Bcrypt. Каждый из этих алгоритмов, поддерживается в PHP. Вы можете использовать Bcrypt, но я приведу другую альтернативу потому что её можно проиллюстрировать и показать вам то, что нужно сделать, чтобы защитить ваши пароли.

Давайте начнем с функции PHP md5() которая может хешировать пароли в соответствии с алгоритмом хеширования MD5. В следующем примере демонстрируется процесс регистрации:

<?php
$username = $_POST["username"];
$password = $_POST["password"];

// создать подключение к базе данных
// ...

// обработка входов
// ...

// создать MD5 хэш пароля
$password = md5($password);

// сохранить эти значения в базе
$sql = "INSERT INTO users (username, password) VALUES (:username, :password)";

$stmt = $db->prepare($sql);

$stmt->execute(array(
    ":username" => $username,
    ":password" => $password
));

А следующий пример показывает процесс аутентификации:

<?php
$username = $_POST["username"];
$password = $_POST["password"];

// создать подключение к базе данных
// ...

// сработал вход
// ...

// создать MD5 хэш пароля
$password = md5($password);

// получить информацию из базы данных
$sql = "SELECT * FROM users WHERE username=:username AND password=:password";
$stmt = $db->prepare($sql);
$stmt->execute(array(
    ":username" => $username,
    ":password" => $password
));

$row = $stmt->fetch();

В приведенном выше примере md5() создает 128-битный хэш из данного пароля. Также можно использовать sha1() вместо md5(), который производит 160-битный хеш (что означает, меньше шансов для столкновения).

Если вы генерируете хеш MD5 строки Мой секретный пароль и вывод пароля в браузер, то выглядеть он будет следующим образом:

7315a012ecad1059a3634f8be1347846

Мой секретный пароль если хэшированние с SHA1 будет следующий результат:

952729c61cab7e01e4b5f5ba7b95830d2075f74b

Никогда не хэшируйте пароли два раза. Это не добавит дополнительную безопасность; скорее это делает хеш слабым и неэффективными. Например, не пытайтесь создать MD5-хеш пароль, а затем предоставить его в качестве вклада в sha1(). Он просто увеличивает вероятность коллизий хеша.

к меню ↑

Применяя хеширование паролей на следующем уровне

Если пробовали, то обнаружили ряд недостатков в алгоритмах SHA1 и MD5. Именно поэтому современные PHP приложения не должны использовать эти две хеш-функции. А должны использовать хеш-алгоритмы из семейства SHA2, такие как SHA256 и SHA512. Из названия видно, что они создают хэш длиной 256 и 512 бит. Они новые и значительно сильнее чем MD5. И число битов больше, значит вероятность столкновений уменьшается. Этих двух алгоритмов более чем достаточно чтобы держать ваш аккаунт в безопасности.

Следующий код показывает, как использовать хеширование SHA256 в PHP:
<?php
$password = hash("sha256", $password);

PHP предлагает встроенную функцию hash(). Первый аргумент функции — это название алгоритма, такие как sha256, sha512, md5, sha1, и многие другие. Второй аргумент — это строка для хеширования. Результат возвращает хешированые строки.

к меню ↑

Использование соли для безопасности

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

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

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

к меню ↑

Демонстрация соли

<?php
define("MAX_LENGTH", 6);

function generateHashWithSalt($password) {
    $intermediateSalt = md5(uniqid(rand(), true));
    $salt = substr($intermediateSalt, 0, MAX_LENGTH);
    return hash("sha256", $password . $salt);
}

Чтобы создать соль мы используем функцию uniqid(). Первый аргумент является rand() — который генерирует случайное число. Вторым аргументом является true — это увеличит шанс уникальности генерируемого числа.

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

к меню ↑

Применить BCrypt

Узнав о MD5/SHA1 и соли, вы получили понимание того, что необходимо для безопасного хранения паролей. Но для реализации серьезной безопасности, существует Bcrypt который является техникой хеширования которую вы должны использовать.

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

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

<?php
function generateHash($password) {
    if (defined("CRYPT_BLOWFISH") && CRYPT_BLOWFISH) {
        $salt = '$2y$11$' . substr(md5(uniqid(rand(), true)), 0, 22);
        return crypt($password, $salt);
    }
}

Функция проверяет доступен ли шифр Blowfish через постоянной CRYPT_BLOWFISH. Если да, то мы генерируем случайные соли. Требованием является то, что соль начинается с «$ 2a $» (или «$ 2y $» смотрите на php.net), чтобы указать алгоритм Blowfish, а затем двузначный номер от 4 до 31. Это номер является параметром, который делает атаку бестолковой и занимает больше времени. Затем мы добавляем алфавитно-цифровую строку, содержащая 22 символа, как основная часть нашей соли. Буквенно-цифровая строка может поддерживает символы такие как ‘.’ и ‘/’.

к меню ↑

Аутентификации пользователей

<?php
function verify($password, $hashedPassword) {
    return crypt($password, $hashedPassword) == $hashedPassword;
}

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

к меню ↑

Заключение

Важной мерой безопасности является хеш паролей ваших пользователей, прежде чем сохранить их в базе данных, при этом использовать современные алгоритмы хеширования такие как Bcrypt, sha256, или sha512. Даже если взломщик получит доступ к вашей базе данных, он не будет иметь реальных паролей ваших пользователей. В этой статье приводятся основные принципы лежащие в основе хеширования соли и Bcrypt.

htmlhook.ru | Скрипты для веб-приложений