HTTP Установка защиты на страницу используя MySQL и PHP. Как я могу использовать базовую HTTP-аутентификацию в PHP

Буквально вчера я решил приступить к разработке личного кабинете одного безымянного сайта. Передо мной встал вопрос по поводу внешнего вида формы авторизации, мне, откровенно, не хотелось заниматься дизайном формы, делать всплывающие подсказки, да и в целом уделять огромное внимание форме. Я сдуру ринулся искать готовые решения, увидел достаточно безвкусных форм. Думал подыскать какой-нибудь готовый компонент, готовое расширение, но в большинстве своём они разочаровали меня. Блуждая по форумам, мне показалось интересным разыграть карту с HTTP Authentication: Basic. Я отправился читать мануал, прежде был недостаточно осведомлён об данном способе. Далее и начались проблемы.

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

RewriteCond %{HTTP:Authorization} ^Basic.* RewriteRule (.*) index.php?authorization=%{HTTP:Authorization}

Увы, у меня возникли проблемы с Joomla, у неё и так всё неплохо в.htaccess, а тут я еще со своим кодом. Уделив время на перебор различных комбинаций, я согласился с тем, что переписывать.htaccess я не буду. С начала код был очень сырым, настолько сырым, что я только-только его скопипастил.

Hello {$_SERVER["PHP_AUTH_USER"]}.

"; echo "

Вы ввели пароль {$_SERVER["PHP_AUTH_PW"]}.

"; } ?>

Замечательно работает, а передо мной стояла цель реализовать авторизацию с помощью использования уже существующих кодовых слов, что расположены в таблице.

get("user") === null) { $db = JFactory::getDbo(); $query = $db->getQuery(true); $query->select("*"); $query->from("#__beda_users"); $query->where("codeword = ".$query->quote($_SERVER["PHP_AUTH_PW"])); if($res = $db->setQuery($query)->loadAssoc()) { $session->set("user", $res); } else { header("WWW-Authenticate: Basic realm="My Realm""); header("HTTP/1.0 401 Unauthorized"); } } else { //Нам бы сюда } } ?>

Работает, значения меняются, сессия создаётся. Замечательно, но в случае с кликом на отмену, значения плевать хотели, что ты отменил, после обновления страницы, возвращаются на место. Я решил, что бороться следует с этим через unset . Я добавил строчку.

If($res = $db->setQuery($query)->loadAssoc()) { $session->set("user", $res); } else { unset($_SERVER["PHP_AUTH_USER"], $_SERVER["PHP_AUTH_PW"]); header("WWW-Authenticate: Basic realm="My Realm""); header("HTTP/1.0 401 Unauthorized"); }

Я старался совладать с этими переменными, но всё глубже уходил в никуда. Устраивал и проверку существования сессии, но тщетно. К тому же мне требовалось сделать завершение сессии, что с слов форумчан мало представляется возможным. Докопался до истины использования адреса вида login :pass@locahost/. Замечательно, переменные изменились $_SERVER["PHP_AUTH_USER"], $_SERVER["PHP_AUTH_PW"] . Увы, но это не стало моим окончательным решением, поскольку после окончания времени сессии $_SERVER["PHP_AUTH_USER"], $_SERVER["PHP_AUTH_PW"] продолжали исполнять. Как и после принудительно присваивания $session->set("user", null). Я вне себя от гнева, но знал, что не прощу себе, если позволю отступиться. Я написал отдельное условие для проверки на логаут.

If($_SERVER["PHP_AUTH_USER"]=="logout"){ $session->set("user", null); header("Refresh: 0;URL="); } if(!isset($_SERVER["PHP_AUTH_PW"])) { header("WWW-Authenticate: Basic realm="My Realm""); header("HTTP/1.0 401 Unauthorized"); } else { //В случае с обнаружением подходящего codeword - создаём сессию if($session->get("user") === null) { $db = JFactory::getDbo(); $query = $db->getQuery(true); $query->select("*"); $query->from("#__beda_users"); $query->where("codeword = ".$query->quote($_SERVER["PHP_AUTH_PW"])); if($res = $db->setQuery($query)->loadAssoc()) { $session->set("user", $res); } else { unset($_SERVER["PHP_AUTH_USER"], $_SERVER["PHP_AUTH_PW"]); header("WWW-Authenticate: Basic realm="My Realm""); header("HTTP/1.0 401 Unauthorized"); } } else { //Нам бы сюда } }

Условие сработало, однако после перезагрузки страницы, когда появляется окно с требованием ввести имя пользователя и пароль , оно их приняло, да после обновление страницы, он предложил мне еще раз ввести пароль, нажму «Отмена», $_SERVER["PHP_AUTH_USER"] будет logout. Обновлю значения, он присвоит их. А нажму на отмену от вернёт прежние значения. Беда .

В конце концов окончательное решение выглядит так.

$session = JFactory::getSession(); function http_auth($session){ $session->set("user", null); unset($_SERVER["PHP_AUTH_PW"], $_SERVER["PHP_AUTH_USER"]); header("WWW-Authenticate: Basic realm="Auth""); header("HTTP/1.0 401 Unauthorized"); } if($_SERVER["PHP_AUTH_USER"]=="logout"){ $session->set("user", null); header("Refresh: 0;URL="); } if($session->get("user") === null){ if(!isset($_SERVER["PHP_AUTH_PW"])){ http_auth($session); } else { $pw = $_SERVER["PHP_AUTH_PW"]; $db = JFactory::getDbo(); $query = $db->getQuery(true); $query->select("*"); $query->from("#__beda_users"); $query->where("codeword = ".$query->quote($pw)); if($res = $db->setQuery($query)->loadAssoc()){ $session->set("user", $res); } else { http_auth($session); } } } else { if(!isset($_SERVER["PHP_AUTH_PW"])){ http_auth($session); } }

Для того, чтобы отправить браузеру клиента сообщение "Authentication Required", что в свою очередь приведет к появлению диалогового окна для ввода имени пользователя и пароля. После того как клиент ввел свое имя и пароль, скрипт будет вызван повторно, но уже с предопределенными переменными PHP_AUTH_USER , PHP_AUTH_PW и AUTH_TYPE , которые соответственно содержат имя пользователя, пароль и тип аутентификации. Эти переменные могут быть найдены в массиве $_SERVER и $HTTP_SERVER_VARS . В настоящее время поддерживается только "Basic"-аутентификация. Также вы можете ознакомится с более детальным описанием функции header() .

Пример фрагмента скрипта, который вынуждает клиента авторизироваться для просмотра страницы:

Пример HTTP-аутентификации

if (!isset($_SERVER [ "PHP_AUTH_USER" ])) {
header ("WWW-Authenticate: Basic realm="My Realm"" );

echo "Текст, отправляемый в том случае,
если пользователь нажал кнопку Cancel"
;
exit;
} else {
echo
"

Hello {$_SERVER["PHP_AUTH_USER"]}.

" ;
echo "

Вы ввели пароль {$_SERVER["PHP_AUTH_PW"]}.

"
;
}
?>

Примечание касательно совместимости: Будьте особенно внимательны при указании HTTP-заголовков. Для того, чтобы гарантировать максимальную совместимость с наибольшим количеством различных клиентов, слово "Basic" должно быть написано с большой буквы "B", регион (realm) должен быть взят в двойный (не одинарные!) кавычки, и ровно один пробел должен предшествовать коду 401 в заголовке HTTP/1.0 401 .

Вместо простого отображения на экране переменных PHP_AUTH_USER и PHP_AUTH_PW , вам, возможно, понадобится проверить их корректность. Используйте для этого запрос к базе данных или поиск пользователя в dbm-файле.

Вы можете пронаблюдать особенности работы браузера Internet Explorer. Он очень требователен к параметру передаваемых заголовков. Указание заголовка WWW-Authenticate перед отправкой статуса HTTP/1.0 401 является небольшой хитростью.

Начиная с PHP 4.3.0, для того, чтобы предотвратить написание кем-либо скрипта, раскрывающего пароль к странице, которая использует внешнюю аутентификацию, переменные PHP_AUTH не устанавливаются в случае, если данная страница использует внешнюю аутентификацию и установлен безопасный режим . Несмотря на это, переменная REMOTE_USER может использоваться для аутентификации пользователя, прошедшего внешнюю аутентификацию. Таким образом, вы всегда можете воспользоваться переменной $_SERVER["REMOTE_USER"] .

Примечание: PHP использует указание директивы AuthType для указания того, используется внешняя аутентификация или нет.

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

И Netscape Navigator и Internet Explorer очищают кеш аутентификации текущего окна для заданного региона (realm) при получении от сервера. Это может использоваться для реализации принудительного выхода пользователя и повторного отображения диалогового окна для ввода имени пользователя и пароля. Некоторые разработчики используют это для ограничения авторизации по времени или для предоставления кнопки "Выход".

Пример HTTP-аутентификации с принудительным вводом новой пары логин/пароль

function authenticate () {
header ("WWW-Authenticate: Basic realm="Test Authentication System"" );
header ("HTTP/1.0 401 Unauthorized" );
echo "Вы должны ввести корректный логин и пароль для получения доступа к ресурсу \n" ;
exit;
}

If (!isset($_SERVER [ "PHP_AUTH_USER" ]) ||
($_POST [ "SeenBefore" ] == 1 && $_POST [ "OldAuth" ] == $_SERVER [ "PHP_AUTH_USER" ])) {
authenticate ();
}
else {
echo
"

Добро пожаловать: {$_SERVER["PHP_AUTH_USER"]}
" ;
echo "Предыдущий логин: {$_REQUEST["OldAuth"]}" ;
echo "

\n" ;
echo "\n" ;
echo "\n" ;
echo "\n" ;
echo "

\n" ;
}
?>

Это поведение не регламентируется стандартами HTTP Basic-аутентификации, следовательно, вы не должны зависеть от этого. Как показали тесты, браузер Lynx не очищает кеш авторизации при получении от сервера статуса 401, и, нажав последовательно "Back", а затем "Forward" возможно открыть такую страницу, при условии, что требуемые атрибуты авториазации не изменились. Однако, пользователь может нажать клавишу "_" для очистки кеша аутентификации.

Также следует заметить, что до версии PHP 4.3.3, HTTP-аутентификация не работала на серверах под управлением Microsoft IIS, если PHP был установлен как CGI-модуль, в силу некоторых ограничений IIS. Для того, чтобы добиться корректной работы в PHP 4.3.3+, вы должны отредактировать конфигурационную настройку IIS под названием "Directory Security". Щелкните на надписи "Edit" и установите опцию "Anonymous Access", все остальные поля должны остаться неотмеченными.

Еще одно ограничение, если вы используете IIS посредством ISAPI: переменные PHP_AUTH_* не определены, но в то же время доступна переменная HTTP_AUTHORIZATION . Пример кода, который вы могли бы использовать: list($user, $pw) = explode(":", base64_decode(substr($_SERVER["HTTP_AUTHORIZATION"], 6)));

Примечание касательно IIS:: Для того, чтобы HTTP-аутентификация корректно работала в IIS, в конфигурации PHP опция cgi.rfc2616_headers должна быть установлена значением 0 (значение по умолчанию).

Внимание: В случае, если используется защищенный режим , UID текущего скрипта будет добавлен в realm -часть заголовка WWW-Authenticate .



<<< Назад Содержание Вперед >>>
Есть еще вопросы или что-то непонятно - добро пожаловать на наш

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

Закрыть такую страницу можно несколькими взаимодополняющими друг друга способами:

  1. Защита паролем (логин/пароль) с помощью переменных $_SERVER["PHP_AUTH_USER"] и $_SERVER["PHP_AUTH_PW"] .
  2. Защита по IP адресу клиента с помощью переменной $_SERVER["REMOTE_ADDR"] .
  3. Защита по MAC адресу в локальных сетях (дополнительно к защите по IP ).

Разберем сначала первый способ, который является основным. Он позволяет закрыть доступ к странице по логину и паролю, таким образом доступ могут получить только люди знающие логин и пароль. К тому же их можно разделять по этому признаку и выдавать соответственно разную информацию для каждого. Реализуется с помощью выдачи специальных полей в заголовке протокола HTTP . Создадим функцию auth_send() :

" ,"

Ошибка аутентификации

" ,"

Обратитесь к администратору для получения логина и пароля.

" ,""; exit; }; ?>

Эту функция сообщает браузеру о том, что для доступа нужна авторизация по логину и паролю. И выдает также страницу в HTML для пользователя.

" ,"

Добро пожаловать!

" ,"

Вы зашли по логину ",$auth_user," и паролю ",$auth_pass,".

" ,""; ?>

Код проверки логина и пароля не слишком сложный в данном случае, так как реализован для одного человека. Логика работы проста, если нет переменной $_SERVER["PHP_AUTH_USER"] и $_SERVER["PHP_AUTH_PW"] или их значения не совпадают с нужными, то вызываете функцию auth_send() . Не забывайте, что в ней в конце вызывается exit , поэтому выполнение программы прекращается.

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

Ваш IP не найден!!!"; exit; }; ?>

Тут в строке $allowed_ips через пробел указаны IP адреса, которым разрешен доступ. Далее получаем массив с помощью explode() и делаем поиск адреса клиента из $_SERVER["REMOTE_ADDR"] . Я для поиска применил функцию array_search() , так как неверняка ее код реализованный на Си будет работать несколько быстрее, чем то, что мы можем написать на PHP с помощью циклов for или foreach . Но скорость тут не главное:)

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

Как линуксоиды уже поняли она основана на ARP таблице системы, доступ к которой можно получить с помощью файла /proc/net/arp . Функция ищет по строкам требуемый IP адрес и возвращает его MAC адрес:

Ваш IP=192.168.10.15 и MAC=00:04:31:E4:F8:37

В системе Windows возможно тоже есть какие-то способы получить MAC попроще, но из тех, которые реально работают, это вывод ARP таблицы системы командой:

C:\WINDOWS\>arp -a Интерфейс: 192.168.10.15 on Interface 0x1000003 Адрес IP Физический адрес Тип 192.168.10.1 00-50-22-b0-6a-aa динамический 192.168.10.2 00-0f-38-68-e9-e8 динамический 192.168.10.3 00-04-61-9e-26-09 динамический 192.168.10.5 00-0f-38-6a-b1-18 динамический

Реализовать защиту на основе этого адреса Вы сможете сами, если Вам это действительно надо:) Но помните, что если у Вас в сети неуправляемое оборудование без возможности привязки MAC адреса к порту, эта защита может не сработать, так как можно подделать все Ваши идентификационные данные используемые для защиты (логин, пароль, IP и MAC адрес).

Ограничение доступа к какой-либо области сайта обычно выглядит
однообразно: каждому пользователю выдается логин и пароль или он сам
их выбирает, и для входа в защищенную часть сайта их нужно ввести. С технической же точки зрения для проверки пароля используются
разные методы. Для ввода логина и пароля может использоваться HTML-форма.
В этом случае пароль передается на сервер открытым текстом в POST-запросе.
Это неприемлемо, если пользователь сидит в локалке, где возможно
использование снифера. Для решения этой проблемы придуман метод
аутентификации с помощью хешей, при котором пароль не передается, а
передается хеш строка, зависящая от пароля, некоего одноразового
параметра и, возможно, еще от каких-либо параметров. Этот метод еще
называют challenge/response, поскольку при его использовании клиент
получает запрос с одноразовым параметром и посылает ответ, содержащий хеш. На уровне протокола HTTP 1.1 возможна аутентификация методом
Basic, что ни чем не лучше использования HTML-формы, и Digest, который
мы и рассмотрим подробно.

При использовании метода Digest, как уже было сказано, пароль
не передается, и его невозможно отснифить, однако есть и другая сторона
проблемы. Для того, чтобы проверить пароль, сервер должен вычислить
ответ и сравнить его с ответом клиента, следовательно, на сервере должен
храниться пароль или зависящие от него данные, необходимые для
прохождения аутентификации. Отсюда следует, что человек, получивший права
на чтение аккаунтов (например, с помощью SQL-injection), сможет получить
доступ к страницам, защищенным методом Digest. При использовании метода
Basic возможно хранение хешей вместо паролей, что не дает поднять права,
прочитав эти хеши (ниже мы увидим, что в Digest тоже могут храниться хеши,
но такие, что их знания достаточно для вычисления ответа). Таким образом, перед нами дилемма: либо наш пароль отснифят,
либо получат через web-уязвимость, которую кто-нибудь обязательно отыщет,
потому что кто ищет, тот всегда найдет. Есть метод аутентификации без
обоих этих недостатков — метод аутентификации на основе открытого ключа:
для проверки нужен открытый ключ, а для прохождения проверки — секретный,
однако в HTTP 1.1 такой метод не предусмотрен. RFC 2069
рекомендует использовать SSL, если защита так важна. Защищается только передача пароля, а контент не шифруется, так
что нет смысла защищать этим методом ресурсы, откуда пользователь
получает секретную информацию. Для них необходим SSL. А имеет смысл
защищать, например, форум или заливку контента на сайт. Итак, если хостинг не поддерживает SSL, а аутентификация должна
быть безопасной, то будем использовать Digest. В Apache предусмотрен модуль mod_digest. Для его использования
в конфиге (или в.htaccess) пишем:

AuthType Digest
AuthUserFile <файл>
AuthName <название защищаемой области>
Require valid_user

Файлы пользователей создаются утилитой
htdigest. Про mod_digest одно время появлялись сообщения, что он уязвим, так что,
возможно, там еще какие-нибудь проблемы обнаружатся. Кроме того, когда
я попытался его использовать у себя дома, получил ошибку
500 Server Internal Error. Кроме того, если добавление аккаунтов должно происходить
автоматически, и их должно быть много, они должны
храниться не в конфиге Апача, а в MySQL. Решение —
использовать PHP. В PHP нет встроенной поддержки этого
метода, поэтому его придется реализовать. Для этого необходимо изучить
этот метод подробно. Сразу замечу, что приведенная в этой статье
реализация работает только на Apache, так как полный доступ к заголовкам
запроса (функция apache_request_headers) работает только в Apache, а на
других серверах может отсутствовать. Нам же просто необходимо прочитать
заголовок Authorization.

Описание метода

Полностью описание метода можно прочитать в RFC 2069, а если
вкратце, то метод работает так. Когда сервер получает запрос, относящийся к защищенной области,
он выдает ошибку 401 Authorization Required и заголовок с запросом
аутентификации такого вида:

WWW-Authenticate: Digest realm=»secure area», nonce=»123456123456″

realm — это название защищенной области, а nonce — одноразовое
значение. Есть еще необязательные параметры, которые мы обсуждать
не будем. Клиент повторяет запрос, добавив к нему заголовок такого вида:

Authorization: Digest realm=»secure area», username=»123″, uri=»/index.php», nonce=»123456123456″, response=»1234567890abcdef1234567890abcdef»

Параметр uri должен совпадать с URI в запросе, а response — это
ответ, который вычисляется так:

response = H(H(A1) + «:» + nonce + «:» + H(A2))
H — хеш-функция, по умолчанию MD5
A1 = логин + «:» + realm + «:» + пароль
A2 = метод запроса + «:» + URI
метод запроса — это GET, POST и тд.

Как видим, A1 не зависит ни от запроса, ни от одноразового
значения, поэтому на сервере может храниться не пароль, а
H(A1). Именно так это реализовано в mod_digest в Apache.
Однако этих же данных достаточно и клиенту. Злоумышленник, получив
этот хеш, может вычислить ответ по приведенным выше формулам и
сформировать HTTP-запрос, например, с помощью программы
AccessDriver и ее инструмента HTTP
Debugger. Подробнее этот процесс будет показан ниже. Сервер должен проверить, является ли одноразовое значение
тем, которое было ранее выдано клиенту и не устарело ли оно.
Если ответ соответствует параметру nonce, но значение этого параметра
не актуально, выдается описанный выше ответ с кодом 401 с той лишь
разницей, что в заголовок WWW-Authenticate добавляется параметр
stale=true, указывающий, что в доступе отказано лишь по этой причине,
и следует повторить попытку, не запрашивая у пользователя новый пароль. Это, имхо, неудобно, поскольку если такая ситуация возникнет
при запросе POST или PUT с большим блоком данных, то клиенту придется
передать все данные дважды. Во избежание этого стандартом предусмотрен
заголовок Authentication-Info, в котором сервер может при ответе на
успешный запрос сообщить клиенту следующее одноразовое значение.
Синтаксис такой же, как у WWW-Authenticate, кроме того что nonce
заменяется на nextnonce. Однако, судя по результатам моих
экспериментов, Opera игнорирует этот заголовок. Другое решение: в соответствии с
RFC 2068 (HTTP/1.1), сервер может ответить раньше, чем завершится запрос,
чтобы клиент прервал ненужную передачу данных, но на Apache+PHP это
не реализуется, поскольку скрипт начинает выполняться только после того,
как Apache полностью получит и пропарсит запрос.

Хранение данных между запросами

В реализации метода challenge/response на PHP есть тонкий момент.
Одноразовый параметр формируется и выдается клиенту в одном ответе, а
проверяется уже в другом сеансе работы скрипта.
То есть его необходимо сохранить от одного вызова скрипта до другого, и для этого придется
использовать файлы или БД. В моем примере используются файлы с именами,
соответствующими одноразовым значениям, а в самих файлах записаны
IP-адреса клиентов, которым они выданы. В примере не реализован сбор
мусора: надо периодически удалять старые файлы.

Разбор кода

Этот скрипт проверяет только пароль, и работает независимо от
логина. В зависимости от успешности проверки выдаются простые ответы.

$realm = ‘secure area’; // Название защищаемой области
$pass = ‘pass’; // Пароль
$fileprefix = ‘./’; //
Путь для файлов-меток, обозначающих валидность nonce

/* Сконструируем одноразовый параметр так, как рекомендуется в RFC2069, хотя можно и по-другому. Параметр, по рекомендации, должен зависеть от адреса клиента, текущего времени и секретной строки. */
$nonce = md5($_SERVER[‘REMOTE_ADDR’] . ‘:’ . time() . ‘:MyCooolPrivateKey’);

// Получаем заголовки
$headers = apache_request_headers();

// Флаг, который мы установим в TRUE при успешной проверке
$auth_success = FALSE;
$stale = «»;

// Если нет заголовка Authorization, то нечего и проверять
if (isset($headers[‘Authorization’]))
{
$authorization = $headers[‘Authorization’];

/* Пропарсим заголовок с помощью регулярного выражения. Заголовок содержит слово «Digest» и список
пареметров вида param="value" или param=value через запятую. Это регулярное выражение соответствует одному такому параметру.
*/
preg_match_all(‘/(,|\s|^)(\w+)=(«([^»]*)»|([\w\d]*))(,|$)/’,
$authorization, $matches, PREG_SET_ORDER);

/* Теперь сформируем для удобства дальнейшей обработки массив, где ключи — названия параметров, а значения элементов массива —
значения параметров.
*/
$auth_params = Array();
for ($i = 0; $i < count($matches); $i++)
{
$match = $matches[$i];

/* Название всегда во второй группе скобок, в значениев зависимости от того, в кавычках оно или нет, может
быть в 4-й или 5-й группе. Для групп скобок, попавших
в нереализованную ветвь, в массиве пустая строка,
поэтому можно просто сложить значения.
*/
$auth_params[$match] = $match . $match;
}

/* Вычислим ответ, который соответствует
логину, введенному пользователем, нашему паролю и одноразовому параметру, переданному пользователем.
*/
$a1 = $auth_params[‘username’] . ‘:’ . $auth_params[‘realm’] . ‘:’ . $pass;
$a2 = $_SERVER[‘REQUEST_METHOD’] . ‘:’ . $_SERVER[‘REQUEST_URI’];
$resp = md5(md5($a1) . ‘:’ . $auth_params[‘nonce’] . ‘:’ . md5($a2));

// Проверяем ответ.
if ($resp == $auth_params[‘response’])
{
//
Проверяем актуальность одноразового параметра
$fn = $fileprefix . $auth_params[‘nonce’];
if (@file_get_contents($fn) == $_SERVER[‘REMOTE_ADDR’])
{
unlink($fn); //
Больше этот параметр неактуален
$auth_success = TRUE; //
Аутентификация пройдена
} else
{
// Одноразовый параметр неактуален
$stale = «, stale=true»;
}
}
}

if ($auth_success)
{
print(«Digest auth test

print(«Successfully authenticated\n»);
var_dump($auth_params);

print(«»);

} else
{
file_put_contents($fileprefix . $nonce, $_SERVER[‘REMOTE_ADDR’]);

$proto = $_SERVER[‘SERVER_PROTOCOL’];
Header(«$proto 401 Not Authorized»);
Header(«WWW-Authenticate: Digest realm=\»$realm\», nonce=\»$nonce\»$stale»);

print(«Digest auth test

»);
print(«You must authenticate with Digest method»);
print(«
»);
}

Прохождение Digest Auth при известном H(A1)

Покажу на примере, как проходить проверку, если пароль неизвестен,
но известен H(A1). Для этого, как уже было сказано, понадобится
AccessDriver. Расчеты хешей я буду делать вызывая из командной строки
PHP CLI. Защищенная страница пусть находится по адресу
http://mrblack.local/auth1.php, а хеш H(A1) равен «a8fb5b2d780a7bf0782207a51a013f04».

Открываем AccessDriver->Tools->HTTP Debugger и вбиваем адрес
«http://mrblack.local/auth1.php». Жмем «Connect». Получаем:

HTTP Header = HTTP/1.1 401 Authorization Required
HTTP Header = Date: Mon, 04 Jul 2005 08:09:17 GMT
HTTP Header = Server: Apache/1.3.31 (Win32) PHP/5.0.2
HTTP Header = X-Powered-By: PHP/5.0.2
HTTP Header = WWW-Authenticate: Digest realm=»secure area», nonce=»5925bea78552224abda11bfe318a8a03″
HTTP Header = Connection: close
HTTP Header = Content-Type: text/html

Открываем консоль, переходим в папку с PHP и вбиваем такую команду:

php -r «print md5(‘a8fb5b2d780a7bf0782207a51a013f04:
: ‘.md5(‘GET:http://mrblack.local/auth1.php’));"

Получаем искомый Digest-ответ: c6d0af0db239d75c
3f59640a4896d096
Теперь в AccessDriver ставим галочку «Header Data», копируем в появившееся
поле заголовки, которые были посланы в прошлом запросе, и дописываем к ним
Authorization. Вот что получается:

GET http://mrblack.local/auth1.php HTTP/1.1
Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/x-shockwave-flash, */*
Accept-Language: en-us,en;q=0.5
User-Agent: Mozilla compatible
Host: mrblack.local
Pragma: no-cache
Authorization: Digest username=»mrblack», realm=»secure area», nonce="5925bea78552224ab
da11bfe318a8a03", uri=»http://mrblack.local/auth1.php», response="c6d0af0db239d75c3f59
640a4896d096"

Жмем «Connect». Получаем результат: