Содержание


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

Часть 12. Базовая идентификация средствами PHP и средствами apache

Comments

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

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

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

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

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

В предыдущих статьях были рассмотрены основные технологии по подготовке СУБД, созданию базового варианта сайта, рассмотрены вопросы, касающиеся создания CMS. В этой статье будут рассмотрены технологии использования идентификации пользователей на сайте средствами PHP и средствами apache, которые будут применяться в дальнейших более широких и масштабных проектах. В конце будет предпринята попытка показа единой, прозрачной для пользователя, системы регистрации.

Базовая аутентификация

На сегодняшний день в сети Интернет базовая аутентификация пользователей является одной из самых распространенных задач. Встроенной возможностью аутентификации обладает протокол HTTP. При использовании этого подхода сценарии или веб-браузеры запрашивают аутентификацию у веб-браузера. После чего, как правило, веб-сервер выводит диалоговое окно, позволяющее вести пользователю свои регистрационные данные. Веб-браузер хранит сведения аутентификации до тех пор, пока открыто его окно, а веб-сервер запрашивает новые сведения аутентификации при каждом новом запросе пользователя. Даже при такой форме взаимодействия браузер не отвечает каждый раз, имея нужную информацию в памяти. Он автоматически отправляет все необходимые сведения без вмешательства пользователя. Именно это свойство протокола HTTP и называют базовой аутентификацией (basic authentication). Базовую аутентификацию можно включать средствами PHP или с помощью механизмов, которыми располагает веб-сервер apache. В этой статье будут рассмотрены методы, предполагающие задействование PHP, сервера apache и IIS.

При базовой аутентификации имя пользователя и пароль передаются в виде открытого текста. Такой способ нельзя назвать безопасным. С приходом протокола HTTP1.1 появился более совершенный способ. Он называется аутентификацией с помощью дайджеста (digest authentication). При этом используется механизм хеширования (как правило, MD5) для сокрытия подробностей выполнения транзакций. Данный тип аутентификации поддерживается практически всеми веб-серверами и почти всеми современными веб-браузерами. К сожалению, версия стандарта, используемая в Microsoft Internet Explorer и Microsoft IIS не совместима с продуктами других компаний.

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

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

Использование базовой аутентификации в PHP

В основе использования базовой аутентификации лежат переменные среды сервера. Зная это, надо иметь ввиду, что сценарий PHP должен определять тип сервера и вести себя так, как, если бы он использовался в качестве модуля сервера apache или ISAPI-модуля сервера IIS. Приводимый ниже сценарий будет одинаково хорошо выполняться на обоих веб-серверах.

<?php

// Если используется сервер IIS, потребуется установить переменные среды 
if (substr($SERVER_SOFTWARE, 0, 9) == 'Microsoft' &&
    !isset($PHP_AUTH_USER) &&
    !isset($PHP_AUTH_PW) &&
    substr($HTTP_AUTHORIZATION, 0, 6) == 'Basic '
   )
{
  list($PHP_AUTH_USER, $PHP_AUTH_PW) =
    explode(':', base64_decode(substr($HTTP_AUTHORIZATION, 6)));
}

// Замените этот оператор if запросом к базе данных или чем-то подобным
if ($PHP_AUTH_USER != 'user' || $PHP_AUTH_PW != 'pass')
{
  // Посетитель еще не предоставил нужную информацию, либо его
  // имя пользователя и пароль неправильны

  header('WWW-Authenticate: Basic realm="Realm-Name"');
  if (substr($SERVER_SOFTWARE, 0, 9) == 'Microsoft')
    header('Status: 401 Unauthorized');
  else
    header('HTTP/1.0 401 Unauthorized');

  echo '<h1>Немедленно покиньте эту страницу!</h1>';
  echo 'Вам не разрешено просматривать данный ресурс.';
}
else
{
  // Посетитель предоставил верную информацию
  echo '<h1>Вы на месте!</h1>';
  echo '<p>Это место и есть то, в которое Вы хотели попасть!.</p>';
}
?>

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

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

Нужно учитывать, что разные браузеры по разному обрабатывают неудачные попытки аутентификации.

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

Использование базовой аутентификации на сервере apache с помощью файлов .htaccess

Результат, описанный выше можно получить и без использования PHP сценариев. Внутренние механизмы сервера apache предоставляют множество технологий использования механизма аутентификации. Один из самых простых – это использование модуля mod_auth , работа которого заключается в сравнении текстовой пары имя + пароль с их аналогами, хранящимися на сервере. Для вывода экрана, похожего на предыдущий пример, нужно создать два HTML-файла. Один для содержимого страницы, другой для сообщения об отказе в доступе. Создание внятной и информативной страницы для отображения сообщения об ошибке или отказе в доступе является хорошим стилем программирования. Учитывая, что такая страница будет отображаться в случае неудачной попытки войти в закрытую зону, может быть полезным в сообщении разместить информацию о возможной регистрации. Полезной может оказаться возможность переопределения пароля и отправка его по электронной почте. Ниже приводимый код реализует некоторые обсуждаемые позиции.

Сообщение об удачной аутентификации:

<html><body>
<h1>Вы на месте!</h1>
<p>Я полагаю, вы счастливы лицезреть эту секретную страницу.</p>
</body></html>

Сообщение об ошибке аутентификации

<html><body>
<h1>Немедленно покиньте эту страницу!</h1>
<p>Вам не разрешено просматривать этот ресурс.</p>
</body></html>

Файл .htaccess

ErrorDocument 401 /chapter16/rejection.html
AuthUserFile /home/book/.htpass
AuthGroupFile /dev/null
AuthName "Realm-Name"
AuthType Basic
require valid-user

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

Реализация системы единой регистрации

Рассмотрим пример реализации системы единой регистрации. Суть ее заключается в том, что если имеется несколько виртуальных магазинов, но в разных доменах, то просто использовать cookie с одного домена для аутентификации пользователя в другом домене нельзя. А задача заключается в обеспечении автоматического размещения персональной информации пользователя, зарегистрированного в одном магазине, в базу данных другого.

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

class SingleSignOn {
    protected $cypher     = 'blowfish';
    protected $mode       = 'cfb';
    protected $key = 'choose a better key';
    protected $td;


    protected $glue = '|';
    protected $clock_skew = 60;
    protected $myversion = 1;

    protected $client;
    protected $server;
    protected $userid;
    public $originating_uri;

    public function __construct() {
      $this->td = mcrypt_module_open ($cypher, '', $mode, '');
    }

    protected function _encrypt($plaintext) {
      $iv = mcrypt_create_iv (mcrypt_enc_get_iv_size ($td), MCRYPT_RAND);
      mcrypt_generic_init ($this->td, $this->key, $iv);
      $crypttext = mcrypt_generic ($this->td, $plaintext);
      mcrypt_generic_deinit ($this->td);
      return $iv.$crypttext;
    }
    protected function _decrypt($crypttext) {
      $ivsize = mcrypt_get_iv_size($this->td);
      $iv = substr($crypttext, 0, $ivsize);
      $crypttext = substr($crypttext, $ivsize);
      mcrypt_generic_init ($this->td, $this->key, $iv);
      $plaintext = mdecrypt_generic ($this->td, $crypttext);
      mcrypt_generic_deinit ($this->td);
      return $plaintext;
    }

    public function generate_auth_request() {
      $parts = array($this->myversion, time(), $this->client, $this->originating_uri);
      $plaintext = implode($this->glue, $parts);
      $request = $this->_encrypt($plaintext);
      header("Location: $client->server?request=$request");
    } 
    public function process_auth_request($crypttext) {
      $plaintext = $this->_decrypt($crypttext);
      list($version, $time, $this->client, $this->originating_uri) = 
        explode($this->glue, $plaintext);
      if( $version != $this->myversion) {
        throw new SignonException("version mismatch");
      }
      if(abs(time() - $time) > $this->clock_skew) {
        throw new SignonException("request token is outdated");
      }
    }
    public function generate_auth_response() {
      $parts = array($this->myversion, time(), $this->userid);
      $plaintext = implode($this->glue, $parts);
      $request = $this->_encrypt($plaintext);
      header("Location: 
	  $this->client$this->originating_uri?response=$request");
    } 
    public function process_auth_response($crypttext) {
      $plaintext = $this->_decrypt($crypttext);
      list ($version, $time, $this->userid) = explode($this->glue, 
	  $plaintext);
      if( $version != $this->myversion) {
        throw new SignonException("version mismatch");
      }
      if(abs(time() - $time) > $this->clock_skew) {
        throw new SignonException("response token is outdated");
      }
      return $this->userid;
    }
  }

protected function _encrypt($plaintext) {
      $iv = mcrypt_create_iv (mcrypt_enc_get_iv_size ($td), MCRYPT_RAND);
      mcrypt_generic_init ($this->td, $this->key, $iv);
      $crypttext = mcrypt_generic ($this->td, $plaintext);
      mcrypt_generic_deinit ($this->td);
      return $iv.$crypttext;
    }
    protected function _decrypt($crypttext) {
      $ivsize = mcrypt_get_iv_size($this->td);
      $iv = substr($crypttext, 0, $ivsize);
      $crypttext = substr($crypttext, $ivsize);
      mcrypt_generic_init ($this->td, $this->key, $iv);
      $plaintext = mdecrypt_generic ($this->td, $crypttext);
      mcrypt_generic_deinit ($this->td);
      return $plaintext;
    }

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

function check_auth() {
    try {
      $cookie = new Cookie();
      $cookie->validate();
    }
    catch(AuthException $e) {
      try {
        $client = new SingleSignOn();
        $client->process_auth_response($_GET['response']);
        $cookie->userid = $client->userid;
        $cookie->set();
      }
      catch(SignOnExcpetion $e) {
        $client->generate_auth_request();
        exit;
      }

страница регистрации:

<?php
  require_once 'Cookie.inc';
  require_once 'SingleSignOn.inc';

  $name = $_POST['name'];
  $password = $_POST['password'];
  $request = $_REQUEST['request'];
  try {
    $signon = new SingleSignOn();
    $signon->process_auth_request($request);
    if($name && $password) {
      $userid = authenticate($name, $password, $signon->client);
    }
    else {
      $cookie = new Cookie();
      $cookie->validate();
      authenticateFromCookie($cookie->userid, $signon->client);
      $userid = $cookie->userid;
    }
    $signon->userid = $userid;
    $resetcookie = new Cookie($userid);
    $cookie->set();
    $signon->generate_auth_reponse();
    return;
  }
  catch (AuthException $e) {
?>
<html>
<title>SingleSignOn Sign-In</title>
<body>
<form name=signon method=post>
Username: <input type="text" name="name"><br>
Password: <input type="password" name="name"><br>
<input type="hidden" name="auth_request" value="<?= $_REQUEST['request'] ?>
<input type=submit name=submitted value="Login">
</form>
</body>
</html>
<?
  }
  catch (SignonException $e) {
    header("HTTP/1.0 403 Forbidden");
  }
?>

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

Выводы

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

Далее мы рассмотрим идентификацию пользователей при помощи mod_auth_mysql.


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


Похожие темы

  • Программирование с использованием PHP и MySQL в разработке Web-приложений. Часть 1.
  • Программирование с использованием PHP и MySQL в разработке Web-приложений. Часть 2.
  • Программирование с использованием PHP и MySQL в разработке Web-приложений. Часть 3.
  • Программирование с использованием PHP и MySQL в разработке Web-приложений. Часть 4.
  • Программирование с использованием PHP и MySQL в разработке Web-приложений. Часть 5.
  • Программирование с использованием PHP и MySQL в разработке Web-приложений. Часть 6.
  • Программирование с использованием PHP и MySQL в разработке Web-приложений. Часть 7.
  • Программирование с использованием PHP и MySQL в разработке Web-приложений. Часть 8.
  • Программирование с использованием PHP и MySQL в разработке Web-приложений. Часть 9.
  • Программирование с использованием PHP и MySQL в разработке Web-приложений. Часть 10.
  • Программирование с использованием PHP и MySQL в разработке Web-приложений. Часть 11.
  • Программирование с использованием PHP и MySQL в разработке Web-приложений. Часть 12.
  • Программирование с использованием PHP и MySQL в разработке Web-приложений. Часть 13.
  • Программирование с использованием PHP и MySQL в разработке Web-приложений. Часть 14.

Комментарии

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

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=40
Zone=Linux, Open source
ArticleID=632408
ArticleTitle=Программирование с использованием PHP и MySQL в разработке Web-приложений: Часть 12. Базовая идентификация средствами PHP и средствами apache
publish-date=03152011