В этой статье мы рассмотрим процесс хэширования паролей, не слишком сложный, но относительно мало распространенный процесс в web-программировании. Хранение паролей пользователей в открытом виде в базе данных - не слишком хороший подход, особенно если к этой базе может получить доступ любой пользователь интернета. Хэширование не панацея, но может значительно уменьшить ущерб в случае кражи данных. Что такое хэш-функция? Это функция, обладающая бесконечной областью определения, но конечной областью значения и одной интересной особенностью: даже при небольшом изменении входного значения значение функции меняется радикально. Основное назначение хэш-функций в криптографии - это генерация ключей
на основе коротких и запоминаемых паролей (слабо запомнить 16 шестнадцатеричных разрядов?).
В нашем примере мы будем рассматривать самый распространенный язык - PHP, в качестве примера - алгоритм Secure Hashing Algorithm 1 (SHA-1). Алгоритм недостаточно сильный и вместо него уже рекомендуют использовать более стойкие, SHA-256 и SHA-512, однако для нашей примерной работы подойдет и он.
Перейдем непосредственно к делу. Работа пользователя начинается с сохранения пароля. поэтому посмотрим как мы можем сохранять хэш пароля пользователя:
<?php
$passwordHash = sha1($_POST['password']);
$sql = 'INSERT INTO user (username,passwordHash) VALUES (?,?)';
$result = $db->query($sql, array($_POST['username'], $passwordHash));
?>
В следующий раз нам надо проверить, правильно ли пользователь ввел пароль? Сделаем это так:
<?php
$passwordHash = sha1($_POST['password']);
$sql = 'SELECT username FROM user WHERE username = ? AND passwordHash = ?';
$result = $db->query($sql, array($_POST['username'], $passwordHash));
if ($result->numRows() < 1)
{ echo 'Sorry, your username or password was incorrect!'; }
else
{ printf('Welcome back %s!', $_POST['username']); }
?>
В PHP мы можем генерировать хэши при помощи md5 или sha1, в первом случае получаем 128-битное значение (32 символа), во втором 160-битный (40 символов).
Например в случае работы такого скрипта:
<?php
$string = 'PHP & Information Security';
printf("Original string: %s\n", $string);
printf("MD5 hash: %s\n", md5($string));
printf("SHA-1 hash: %s\n", sha1($string));
?>
Мы можем получить примерно следующее:
Original string: PHP & Information Security
MD5 hash: 88dd8f282721af2c704e238e7f338c41
SHA-1 hash: b47210605096b9aa0129f88695e229ce309dd362
В MySQL мы можем генерировать хэши внутренними функциями password(), md5() или sha1, первая используется во встроенном механизме аутентификации сервера баз данных. Однако использовать его не рекомендуется, так как до версии 4.1 он был очень уязвим.
Естественно, что функции работают одинаково, поэтому можно проверять/сохранять хэши не средствами PHP, а средствами самого MySQL:
<?php
$sql = 'INSERT INTO user (username, passwordHash) VALUES (?, SHA1(?))';
$result = $db->query($sql, array($_POST['username'], $_POST['password']));
?>
Как можно улучшить алгоритм? Ответ очевиден - перед хэширование мы создаем случайную строку определенной длинны, соль, и присоединяем ее к паролю. Этим мы убиваем сразу двух зайцев - взломщику не будут видны одинаковые пароли и мы улучшим стойкость к перебору за счет увеличения длинны. Естественно, соль придется хранить в базе, однако это самое меньшее зло, несколько лишних символов в базе проблемы не решат.
<?php
define('SALT_LENGTH', 9);
function generateHash($plainText, $salt = null)
{
if ($salt === null)
{
$salt = substr(md5(uniqid(rand(), true)), 0, SALT_LENGTH);
}
else
{
$salt = substr($salt, 0, SALT_LENGTH);
}
return $salt . sha1($salt . $plainText);
}
?>
В данном случае мы генерируем хэш используя текстовый пароль и случайную соль. Таким образом мы записываем в базу модифицированный хэш. При проверке достоверности пароля действуем наоборот: получаем из базы соль и соединяем ее с паролем, потом сравниваем с хранящимся в БД хэшем.
Итого
Мы посмотрели как просто реализовать защиту. Однако приведенные выше примеры можно рассматривать только как начальную точку для ваших собственных PHP разработок. Программируйте в удовольствие!