Содержание


Программирование с использованием PHP и MySQL в разработке Web-приложений

Часть 3. Решение задачи аутентификации посетителей Web-сайта. Технологии хранения паролей

Comments

Серия контента:

Этот контент является частью # из серии # статей: Программирование с использованием PHP и MySQL в разработке Web-приложений

Следите за выходом новых статей этой серии.

Этот контент является частью серии:Программирование с использованием PHP и MySQL в разработке Web-приложений

Следите за выходом новых статей этой серии.

Интернет – среда анонимная, но иногда нужно иметь данные о посетителях сайта. Однако эти данные могут быть незначительны, если они получены без личного участия самих посетителей. Так, на стороне сервера без участия пользователя может быть получена информация о версии Web-браузера, версии и типе операционной системы. Можно узнать информацию о разрешении и цветовой палитре монитора, определить размер окон браузера. Помимо общих данных, возможно получение ip-адреса и места его «прописки» – географическое расположение. Но вся эта информация не дает возможности установить личные данные самого пользователя, которые затем можно использовать для обратной связи с ним. Это может понадобиться для исключения необходимости ввода каждый раз зарегистрированным пользователем своих данных при обращении к разным сервисам сайта. Например, при подобном подходе клиенту не придется всякий раз указывать сведения о себе для доставки товаров из интернет-магазина на дом.

Запросив и получив такую информацию о пользователе, ее нужно с ним связать и использовать при его последующих посещениях. В таких случаях применяют аутентификацию – предложение посетителю сайта подтвердить свою личность. При этом подразумевается ввод им уникального имени и пароля. Рассмотрим простой пример реализации контроля доступа аутентифицируемого пользователя на сайт.

Реализация контроля доступа

Скрипт в примере выводит одну из трех возможных страниц (областей). Если файл загружается без требуемых параметров о посетителе, то выводится форма для заполнения данных (рисунок 3). При вводе «правильных» данных отображается секретная область, в противном случае – сообщение с предложением удалиться.

Рисунок 3.
Рисунок 3.
Рисунок 3.
<?php
    // Короткие имена переменных
@ $name = $_POST['name'];
@ $password = $_POST['password'];
  if(empty($name)||empty($password))
  {
    // Ввод Посетителем имени и пароля
?>
    <h1>Введите свое имя пользователя и пароль</h1>
    Эта область является секретной.
    <form method = "post" action = "secret.php">
    <table border = "1">
    <tr>
      <th> Имя пользователя </th>
      <td> <input type = "text" name = "name"> </td>
    </tr>
    <tr>
      <th> Пароль </th>
      <td> <input type = "password" name = "password"> </td>
    </tr>
    <tr>
      <td colspan = "2" align = "center">
        <input type = "submit" value = "Войти">
      </td>
    </tr>
    </table>
    </form>
<?php
  }
  else if($name=='user' && $password=='pass')
  {
    // имя пользователя и пароль верны
    echo '<h1>Вы в секретной области</h1>';
    echo 'Поздравляем вас :-)';
  }
  else
  {
    // имя и пароль посетителя не верны
    echo '<h1>Уходите - Вы не зарегистрированы!</h1>';
    echo 'Вам не разрешено здесь находиться.';
  }
?>

В общем, представленный сценарий справляется с поставленной задачей, но имеет ряд недостатков.

  1. В нем поддерживается только одна комбинация имени пользователя и пароля, хранимая в самом сценарии.
  2. Данные в сценарии сохраняются в незащищенном виде.
  3. Сценарий защищает только одну область.
  4. Пароль из формы передается в виде открытого текста.

Решение этих вопросов реализуется применением нескольких технологий. Первая из них – сохранять пароли в базе данных, а не в скрипте. Такое решение усложнит сценарий, но позволит быстро проводить аутентификацию большого числа пользователей с хорошей скоростью. Ниже приводится сценарий с использованием СУБД MySQL.

<?php
 //ввод в работу коротких имен переменных
 $name = $HTTP_POST_VARS['name'];
 $password = $HTTP_POST_VARS['password'];
  if(!isset($HTTP_POST_VARS['name'])&&!isset
  ($HTTP_POST_VARS['password']))
  {
    //ввод пароля и имени
?>
    <h1>Введите свое имя</h1>
    Это секретная страница.
    <form method="post" action="secretdb.php">
    <table border="1">
    <tr>
      <th> Ваше имя </th>
      <td> <input type="text" name="name"> </td>
    </tr>
    <tr>
      <th> Пароль </th>
      <td> <input type="password" name="password"> </td>
    </tr>
    <tr>
      <td colspan="2" align="center">
        <input type="submit" value="Log In">
      </td>
    </tr>
    </table>
    </form>
<?php
  }
  else
  {

    // соединение с СУБД mysql
    $mysql = mysql_connect( 'localhost', 'webauth', 'webauth' );
    if(!$mysql)
    {
      echo 'Нет соединения с базой данных.';
      exit;
    }
    // выбор требуемой БД
    $mysql = mysql_select_db( 'auth' );
    if(!$mysql)
    {
      echo 'База данных не выбрана.';
      exit;
    }

    // запрос о базе данных для выборки совпадения
    $query = "select count(*) from auth where
              name = '$name' and
              pass = '$password'";

    $result = mysql_query( $query );
    if(!$result)
    {
      echo 'Не могу выполнить запрос.';
      exit;
    }

    $count = mysql_result( $result, 0, 0 );

    //трассировка результата

    echo "<br>name=$name";
    echo "<br>password=$password";
    echo "<br>result=$result";
    echo "<br>count=$count";

    if ( $count > 0 )
    {
      // имя и пароль верны
      echo '<h1>Вы в секретной области!</h1>';
      echo 'Здесь вы узнаете все секреты.';
    }
    else
    {
      // имя и пароль не верны
      echo '<h1>Уходите или будет вызвана милиция!</h1>';
      echo 'Вы не авторизованы для просмотра этого ресурса.';
    }
  }
?>

SQL-код, реализующий базу данных, участвующую в работе скрипта, имеет вид:

create database auth;
use auth;
create table auth (
        name            varchar(10) not null,
        pass            varchar(30) not null,
        primary key     (name)
);

insert into auth values
  ('user', 'pass');
insert into auth values
  ( 'testuser', 'test123' );
grant select, insert, update, delete
on auth.*
to webauth@localhost
identified by 'webauth';

SQL-код, представленный в примере, создает базу данных auth, переходит к ней, а затем создает таблицу с полями name и pass (рисунок 1). В поля заносятся данные в виде записей user, pass, testuser и test123, что представляет собой попарно учетные записи и пароли, с которыми можно попасть в секретную область и увидеть защищаемую информацию. Затем пользователю webauth@localhost задаются привилегии select, insert, update, delete на базу auth и задается пароль webauth. Дело в том, что соединение с базой и все действия, определенные заданными правами из скрипта, будут осуществляться от имени пользователя webauth@localhost.

Рисунок 1.
Рисунок 1.
Рисунок 1.
Рисунок 2.
Рисунок 2.
Рисунок 2.

Скрипт работает следующим образом. Если не введены учетные запись или пароль, то пользователю предлагается форма для их ввода (рисунок 3). Форма предлагает ввести имя и пароль, и при нажатии на “Log in” отправляет введенное методом post на вход скрипту secretdb.php. Если данные введены, то предпринимается попытка установить соединение с базой данных. В случае неудачи выводится сообщение «Нет соединения с базой данных». При удачном соединении предпринимается попытка выбора требуемой базы; если это не получилось, выводится сообщение «База данных не выбрана», если все прошло хорошо, но запрос к базе не прошел, то будет вывод «Не могу выполнить запрос». В альтернативном случае делается выборка по совпадению имени и пароля. Если такое совпадение есть ($count > 0), то печатается содержимое секретной области, если совпадений нет, то выводится сообщение: «Уходите или будет вызвана милиция! Вы не авторизованы для просмотра этого ресурса».

Рисунок 3.
Рисунок 3.
Рисунок 3.

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

В PHP имеется несколько таких функций. Одна из самых старых и наименее защищенных – это crypt(). Есть и более надежные алгоритмы, реализованные в таких функциях, как md5(). Если не нужна совместимость со старыми версиями PHP, то можно использовать алгоритмы Secure Hash Algorithm (SHA-1).

PHP-функция sha1() реализует строгий однонаправленный хеш-алгоритм. Прототип функции имеет вид:

	string sha1 (string str [, bool raw_output])

Получая на вход строку, функция возвращает псевдослучайную 40-символьную строку. Если в качестве параметра raw_output передать true, то функция вернет 20-символьную строку двоичных данных.

Самое полезное в работе SHA1 это то, что ее результат всегда строго определен. Для одной и той же строки она будет возвращать всегда один и тот же результат. Таким образом, вместо кода:

	if ( $username == ‘user’ && $password == ‘pass’)
	{
		// пароль совпадает
	}

можно записать следующее:

	if ( $username == ‘user’ &&
		sha1($password) ==  ‘вычисленное значение sha1 для password’ )
	{
		//пароли совпадают
	}

Единственное, что нужно знать до использования функции sha1() – это то, как выглядела сама строка пароля.

Для хранения паролей в базе данных MySQL можно использовать функцию sha1() или воспользоваться MySQL-функцией SHA1(). При использовании последнего варианта листинг SQL-кода немного видоизменится, а именно:

	$query = select count(*) from auth where
               name = '$name' and
               pass = sha1('$password')

Этот запрос будет считать количество строк в таблице auth, в которых значение поля name совпадает со значением переменной $name, а значение поля password совпадет со значением, полученным от применения функции SHA1() к переменной $password. С учетом того, что пользователи выбирают уникальные имена и пароли, результатом запроса может быть 1 или ноль. Нужно помнить, что хеш-функция возвращает фиксированное значение в общем случае. В случае SHA1() это 40 символов в виде строки. Поэтому при проектировании таблицы нужно учитывать правильные размеры столбцов по ширине.

Защита нескольких областей с данными

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

Наиболее простой способ защиты нескольких страниц – это использование механизма контроля доступа, который может предоставить Web-сервер. Возможен самостоятельный метод создания таких механизмов, но для этого нужно включить части приведенного ранее сценария в код каждой страницы, нуждающейся в защите. С помощью директив auto_prepend_file и auto_append_file нужные файлы можно поместить в начало и конец каждого файла в требуемых каталогах. Файл php.ini поддерживает две опции конфигурирования – auto_prepend_file и auto_append_file. Настроив их нужным образом, они укажут на те файлы, которые необходимы для загрузки до и после каждой страницы соответственно приведенным операторам. Такие файлы будут вести себя так, как будто они включены с помощью оператора include(). Если файл не существует, то будет выдаваться предупреждение. Например, в среде Windows их использование будет иметь вид:

	auto_prepend_file = “C:/www/include/my_folder/my_file.php”

а в среде UNIX – соответственно:

	auto_prepend_file = “/home/username/include/my_file.php”

При использовании этих директив отпадает необходимость применения каждый раз операторов, подобных include(), include_once(), require(), require_once().

Операторы include() и require() обеспечивают повторное использование любого типа кода. С их помощью загружают файлы прямо в PHP-сценарий. Такой файл может содержать PHP-операторы, текст, HTML-дескрипторы, PHP-функции или классы. Работа этих операторов похожа на серверные включения (server-side include – SSI), которые поддерживаются многими Web-серверами, а также операторами #include в языках C и С++.

Приведем простой пример использования такого оператора. Если предположить, что имеется файл с именем use.php, который содержит следующий код:

	<php
		echo ‘Это простой PHP оператор. <br />’;
	?>

и файл main.php, который содержит код:

	<php
		echo ‘Это главный файл. <br />’;
		require( ‘use.php’ );
		echo ‘Здесь сценарий завершается. ,br /> ’;
	?>

то при вызове файла main.php в браузере отразится:

	простой PHP оператор.
	Это главный файл.
	Здесь сценарий завершается.

Таким образом, при выполнении основного сценария оператор require() заменяется содержимым запрошенного файла, после чего сценарий выполняется.

Оператор include() практически ничем не отличается от рассмотренного выше require(). Отличие между ними состоит в том, что при неудачном выполнении конструкция require() выдает сообщение о неисправимой ошибке, а include() – только предупреждение.

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

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

Многие из приведенных вопросов имеют более элегантное и разноплановое решение с применением базовой аутентификации протокола HTTP, базовой аутентификации с помощью PHP, с помощью файла .htaccess сервера Apache и модуля mod_auth_mysql. Эти вопросы будут рассмотрены в следующих статьях.

Выводы

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

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

Рассмотрены программные реализации вопросов контроля доступа к защищаемым ресурсам. Приведен скрипт, работающий совместно с HTML-формой и реализующий такой контроль.

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

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

Описаны нюансы возможной защиты нескольких объектов (страниц). Пояснены вопросы применения директив auto_prepend_file и auto_append_file и настроек файла php.ini. Описана функциональность работы операторов include(), include_once(), require(), require_once().

<< Предыдущая статья


Ресурсы для скачивания


Комментарии

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

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=40
Zone=Linux, Open source
ArticleID=501961
ArticleTitle=Программирование с использованием PHP и MySQL в разработке Web-приложений: Часть 3. Решение задачи аутентификации посетителей Web-сайта. Технологии хранения паролей
publish-date=07222010