Безвременье auth php. Прохождение Digest Auth при известном H(A1). Где можно реализовать

Ограничение доступа к какой-либо области сайта обычно выглядит
однообразно: каждому пользователю выдается логин и пароль или он сам
их выбирает, и для входа в защищенную часть сайта их нужно ввести. С технической же точки зрения для проверки пароля используются
разные методы. Для ввода логина и пароля может использоваться 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». Получаем результат:

5 ответов

Как работает PHP? Если это произойдет через Apache mod_cgi, я боюсь, что вы не сможете получить информацию об аутентификации. Apache не передаст его в приложения CGI, если вы не скомпилируете его с флагом SECURITY_HOLE_PASS_AUTHORIZATION . (Независимо от того, действительно ли это отверстие безопасности или нет, зависит от того, имеют ли другие пользователи на вашем сервере более низкие или более высокие привилегии, чем пользователи вашего сайта.)

Если это IIS CGI, вы должны убедиться, что настроен только "Анонимный доступ к каталогу", или IIS попытается выполнить вход и аутентификацию самих учетных данных.

Echo "

Hello {$_SERVER["PHP_AUTH_USER"]}.

";

Отверстие безопасности в HTML-инъекции (ну, если оно сработало). Используйте htmlspecialchars() . Человек, это страница PHP doc, полная очень сомнительных советов и кода.

Вы также можете посмотреть на $_SERVER["HTTP_AUTHORIZATION"] , хотя я подозреваю, что это проблема mod_cgi, и в этом случае ни одна из переменных не будет присутствовать, и вам придется прибегать к имени на основе файлов cookie. Или перейдите на хост с более современным подходом к хостингу PHP, например FastCGI или mod_php.

в.htaccess добавьте это:

RewriteEngine on RewriteRule .* -

и в начале вашего script добавьте это:

List($_SERVER["PHP_AUTH_USER"], $_SERVER["PHP_AUTH_PW"]) = explode(":" , base64_decode(substr($_SERVER["HTTP_AUTHORIZATION"], 6)));

Попробуйте следующее:

\n"); print("\n"); print("Listing 24-1\n"); print("\n"); print("\n"); } function endPage() { print("\n"); print("\n"); } /* ** test for username/password */ if((isset($_SERVER["PHP_AUTH_USER"]) && ($_SERVER["PHP_AUTH_USER"] == "leon")) AND (isset($_SERVER["PHP_AUTH_PW"]) && ($_SERVER["PHP_AUTH_PW"] == "secret"))) { startPage(); print("You have logged in successfully!
\n"); endPage(); } else { //Send headers to cause a browser to request //username and password from user header("WWW-Authenticate: " . "Basic realm=\"Leon Protected Area\""); header("HTTP/1.0 401 Unauthorized"); //Show failure text, which browsers usually //show only after several failed attempts print("This page is protected by HTTP " . "Authentication.
\nUse leon " . "for the username, and secret " . "for the password.
\n"); } ?>

Убедитесь, что вы переопределили переменную $_SERVER[‘PHP_AUTH_USER’] и $_SERVER[‘PHP_AUTH_PW’] до того места, где вы пытаетесь получить доступ к обеим переменным.

Если выше не так, тогда используйте chek, если вы используете php как cgi mod или нет. Если вы используете php как cgi mod, то в большинстве случаев вы не получите $_SERVER[‘PHP_AUTH_USER’] и $_SERVER[‘PHP_AUTH_PW’] , потому что в общем случае мы не скомпилируем apache с опцией SECURITY_HOLE_PASS_AUTHORIZATION Поэтому, если вы хотите получить обе переменные, тогда перекомпилируйте apache с параметром SECURITY_HOLE_PASS_AUTHORIZATION или вы можете использовать подход.htaccess, описанный в

Для того, чтобы отправить браузеру клиента сообщение "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 .



<<< Назад Содержание Вперед >>>
Есть еще вопросы или что-то непонятно - добро пожаловать на наш
Буквально вчера я решил приступить к разработке личного кабинете одного безымянного сайта. Передо мной встал вопрос по поводу внешнего вида формы авторизации, мне, откровенно, не хотелось заниматься дизайном формы, делать всплывающие подсказки, да и в целом уделять огромное внимание форме. Я сдуру ринулся искать готовые решения, увидел достаточно безвкусных форм. Думал подыскать какой-нибудь готовый компонент, готовое расширение, но в большинстве своём они разочаровали меня. Блуждая по форумам, мне показалось интересным разыграть карту с 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); } }