Для хранения паролей существует множество месть, более подходящих, чем код сценария. Внутри сценария очень трудно изменить данные. Можно написать сценарий, который будет изменять себя, но это не особо удачная идея. При этом на сервере придется хранить выполняющийся на нем сценарий, доступный для записи и изменений со стороны других пользователей. Хранение паролей на сервере в отдельном файле позволит без труда написать программу для добавления и удаления пользователей, а также для изменения паролей.
Внутри сценария или другого файла данных существует ограничение на количество пользователей, которых можно обслужить без серьезного снижения общей производительности сценария. Если планируется сохранить большое количество элементов в файле или производить поиск среди большого числа элементов, то следует рассмотреть возможность использования базы данных вместо двумерного файла. В качестве практического совета можно принять следующее утверждение: если вы собираетесь хранить и производить поиск в более чем 100 элементах, следует отдать предпочтение базе данных.
Использование базы данных для хранения имен и паролей посетителей не сильно усложнит сценарий, но позволит быстро проводить аутентификацию множетсва пользователей. Это также упростит создание сценария для добавления и удаления пользователей, а также даст возможность пользователям изменять свои пароли.
Прежде чем приступить к созданию скрипта, создадим базу данных test, с которой будем эксперементировать.
create database test;
use test;
Создадим таблицу autohor_users, в которой будут храниться имена пользователей и их пароли.
CREATE TABLE author_users (
name VARCHAR( 20 ) NOT NULL ,
pass VARCHAR( 40 ) NOT NULL ,
PRIMARY KEY ( name )
);
Создадим запросы базе данных для двоих пользователей.
INSERT INTO author_users VALUES ('username', 'password');
INSERT INTO author_users VALUES ('testuser', SHA1('password');
secretdb.php —использование MySQL для улучшения простого механизма аутентификации
<?php
//создание коротких имен переменных
@ $name = $_POST[name];
@ $password = $_POST[password];
$title ="Пожалуйста, введите свое имя пользователя и пароль";
if (!isset($_POST[name])&&!isset($_POST[password]))
{
// Посетитель должен ввести имя и пароль
?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=windows-1251" />
<title><?php echo $title ?></title>
</head>
<body>
<h1><?php echo $title ;?></h1>
<p>Эта страница является секретной.</p>
<form action="secretdb.php" method="post">
<table width="49%" border="0" cellspacing="0" cellpadding="0">
<tr>
<td width="27%">Имя пользователя</td>
<td width="73%"><input name="name" type="text" size="25" maxlength="50" /> </td>
</tr>
<tr>
<td>Пароль</td>
<td><input type="password" name="password" size="25" maxlength="50" /></td>
</tr>
<tr>
<td></td>
<td colspan="2">
<input type="submit" name="button" value="Войти" />
</td>
</tr>
</table>
</form>
<?php
}
else
{
// Подключение к MySQL
$mysql = mysqli_connect ('localhost', 'webauth', 'webauth');
if (!$mysql)
{
echo 'Невозможно подключиться к базе данных';
exit;
}
// Выбор соответствующей базы
$selected = mysqli_select_db ($mysql, 'test');
if (!$mysql)
{
echo 'Невозможно выбрать базу данных.';
exit;
}
// Запрос к базе данных с целью выяснения существования соответствующей записи
$query = "select count (*) from author_users where
name = '$name' and
pass = '$password'";
$result = mysqli_query ($mysql, $query);
if(!$result)
{
echo 'Невозможно выполнить запрос';
exit;
}
$row = mysqli_fetch_row($result);
$count = $row[0];
if ($count > 0)
{
// Комбинация имени пользователя и пароля посетителя верна
echo '<h1>Вы зашли на секретную страницу</h1>';
echo 'Добро пожаловать! Имя пользователя и пароль введены правильно';
}
else
{
// Комбинация имени и пароля неверна
echo '<h1>Неправильно введены данные</h1>';
echo 'Вам не разрешено просматривать данный ресурс.';
}
}
?>
</body>
</html>
В окне веб-браузера это будет выглядеть ТАК.
Протестируйте форму. Вы увидите, что при вводе в форму зарегистрированных в базе данных пользователей и их паролей, вы получите разные результаты. Почему это происходит мы узнаем дальше.
Независимо от того, где храняться пароли — в базе данных или в файле — хранение паролей в виде простого текста сопряжено с неоправданным риском. Однонаправленный алгоритм хэширования обеспечивает дополнительную защиту при незначительных дополнительных затратах.
PHP предоставляет несколько однонаправленных хеш-функций. Наиболее старой и наименее защищенной из них является crypt(). Алгоритм вычисления дайджеста Message Digest 5 (MD5), реализованный в функции md5(), более строг и доступен в последних версиях PHP. Если вам не нужна совместимость со старыми версиями PHP, пользуйтесь алгоритмом Secure Hash Algoritm 1 (SH1).
PHP-функция sha1() реализует строгий однонаправленный криптографический хеш-алгоритм. Прототип этой функции выглядит следующим образом:
string sha1 (string str [ , bool raw_output] )
Получив на входе строку str, эта функция возвращает псевдослучайную 40-символьную строку. Если в качестве raw_output передать true, функция возвратит 20-символьную строку двоичных данных. Например для строки "password" функция sha1() вернет строку "5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8". Эта строка не может быть дешифрована и превращена обратно в "password" даже ее создателем, поэтому на первый взгляд она может казаться не столь уж полезной. Что делает функцию sha1() полезной, так это то, что ее результат строго детермирован. Для одной ти той строки sha1() будет возвращать один и тот же результат при каждом ее вызове.
Таким образом, вместо следующего PHP-кода:
if ($username == 'user' && $password == 'pass')
{
// Пароль совпадает
}
можно воспользоваться кодом:
if ($username == 'user' && sha1($password) == '5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8')
{
// Пароль совпадает
}
Перед использованием функции sha1() нам не требуется знать, как выглядела строка пароля. Достаточно знать, совпадает ли пароль с тем, для которого применялась sha1().
Как уже упоминалось, жесткое кодирование правильных имен и паролей посетителей в сценарии — совершенно неудачная идея. Для их хранения следует использовать отдельный файл или базу данных.
Если для хранения аутентификации выбрана база данных MySQL, можно воспользоваться PHP-функцией sha1(). MySQL предлагает большой спектр алгоритмов хеширования по сравнению с PHP, однако все они служат одной и тойже цели.
Чтобы воспользоваться функцией sha1(), SQL-запрос следует переписать, следующим образом:
select count (*) from author_users where
name = '$username' and
password = sha1('$password')
Этот запрос будет подсчитывать количество строк в таблице author_users, в которых значение поля name совпадает с содержимым переменной $name, а значение поля password совпадает с результатом применения функции SHA1() к переменной $password. Учитывая, что заставляем посетителей выбирать уникальные имена, результатом запроса может быть 0 или 1.
Не забывайте, что хеш-функции в общем случае возвращают данные фиксированного размера. В случае SHA1() это 40 символов при представлении в виде строки. Убедитесь, что соответствующий столбец в базе данных имеет достаточную ширину.
Еще раз просмотрев код, легко заметить, что мы создали одного пользователя ('username') с незашифрованным паролем и второго пользователя ('testuser') — с зашифрованным паролем.