Выход из аутентификации HTTP через PHP
Как правильно выйти из защищенной папки HTTP-аутентификации?
Есть обходные пути, которые могут достигнуть этого, но они потенциально опасны, потому что они могут содержать ошибки или не работать в определенных ситуациях / браузерах. Вот почему я ищу правильное и чистое решение.
19 ответов
Mu. Не существует правильного способа, даже не совместимого в разных браузерах.
Это проблема, вытекающая из спецификации HTTP (раздел 15.6):
Существующие HTTP-клиенты и пользовательские агенты обычно сохраняют информацию аутентификации на неопределенный срок. HTTP/1.1. не предоставляет способ для сервера, чтобы направить клиентов отказаться от этих кэшированных учетных данных.
С другой стороны, в разделе 10.4.2 говорится:
Если в запрос уже включены учетные данные авторизации, то ответ 401 указывает, что в авторизации было отказано для этих учетных данных. Если ответ 401 содержит ту же проблему, что и предыдущий ответ, и пользовательский агент уже предпринял попытку аутентификации, по крайней мере, один раз, тогда пользователю СЛЕДУЕТ представить объект, который был указан в ответе, поскольку этот объект может включать в себя соответствующую диагностическую информацию.
Другими словами, вы можете снова показать окно входа в систему (как говорит @Karsten), но браузер не должен выполнять ваш запрос - так что не слишком зависите от этой (неправильной) функции.
Метод, который хорошо работает в Safari. Также работает в Firefox и Opera, но с предупреждением.
Location: http://logout@yourserver.example.com/
Это говорит браузеру открыть URL с новым именем пользователя, переопределяя предыдущее.
Простой ответ заключается в том, что вы не можете надежно выйти из http-аутентификации.
Длинный ответ:
Http-auth (как и остальная часть спецификации HTTP) подразумевает отсутствие состояния. Таким образом, "войти в систему" или "выйти из системы" на самом деле не имеет смысла. Лучший способ убедиться в этом - спросить для каждого HTTP-запроса (и помните, что загрузка страницы обычно состоит из нескольких запросов): "Вам разрешено делать то, что вы запрашиваете?". Сервер видит каждый запрос как новый и не связанный с любыми предыдущими запросами.
Браузеры решили запомнить учетные данные, которые вы сообщаете им на первых 401, и повторно отправлять их без явного разрешения пользователя на последующие запросы. Это попытка дать пользователю ожидаемую модель "вошел / вышел", но это просто помеха. Это браузер, который имитирует это постоянство состояния. Веб-сервер совершенно не знает об этом.
Таким образом, "выход из системы" в контексте http-auth является чисто симуляцией, предоставляемой браузером, и поэтому находится вне полномочий сервера.
Да, есть кладжи. Но они нарушают RESTful-ность (если это имеет ценность для вас), и они ненадежны.
Если вам абсолютно необходима модель входа в систему / выхода из нее для проверки подлинности вашего сайта, лучшим вариантом будет файл cookie для отслеживания, в котором каким-либо образом хранится состояние сервера (mysql, sqlite, flatfile и т. Д.). Это потребует оценки всех запросов, например, с помощью PHP.
Временное решение
Вы можете сделать это, используя Javascript:
<html><head>
<script type="text/javascript">
function logout() {
var xmlhttp;
if (window.XMLHttpRequest) {
xmlhttp = new XMLHttpRequest();
}
// code for IE
else if (window.ActiveXObject) {
xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
}
if (window.ActiveXObject) {
// IE clear HTTP Authentication
document.execCommand("ClearAuthenticationCache");
window.location.href='/where/to/redirect';
} else {
xmlhttp.open("GET", '/path/that/will/return/200/OK', true, "logout", "logout");
xmlhttp.send("");
xmlhttp.onreadystatechange = function() {
if (xmlhttp.readyState == 4) {window.location.href='/where/to/redirect';}
}
}
return false;
}
</script>
</head>
<body>
<a href="#" onclick="logout();">Log out</a>
</body>
</html>
Что сделано выше, это:
для IE - просто очистить кеш аутентификации и перенаправить куда-нибудь
для других браузеров - отправьте запрос XMLHttpRequest за кулисами с именем входа и паролем. Нам нужно отправить его по какому-либо пути, который вернет 200 OK на этот запрос (т.е. он не должен требовать HTTP-аутентификации).
замещать '/where/to/redirect'
с некоторым путем для перенаправления после выхода из системы и замены '/path/that/will/return/200/OK'
с некоторым путем на вашем сайте, который вернет 200 ОК.
Обходной путь (не чистое, хорошее (или даже работающее! См. Комментарии) решение):
Отключить его учетные данные один раз.
Вы можете переместить свою логику HTTP-аутентификации в PHP, отправив соответствующие заголовки (если не вошли в систему):
Header('WWW-Authenticate: Basic realm="protected area"');
Header('HTTP/1.0 401 Unauthorized');
И синтаксический анализ ввода с:
$_SERVER['PHP_AUTH_USER'] // httpauth-user
$_SERVER['PHP_AUTH_PW'] // httpauth-password
Так что отключение его учетных данных один раз должно быть тривиальным.
Выйти из HTTP Basic Auth в два этапа
Допустим, у меня есть область HTTP Basic Auth с именем "Защищено паролем", и Боб вошел в систему. Чтобы выйти из системы, я делаю 2 AJAX-запроса:
- Сценарий доступа /logout_step1. Он добавляет случайного временного пользователя в.htusers и отвечает его логином и паролем.
- Доступ к скрипту /logout_step2, аутентифицированному с использованием временного имени пользователя и пароля. Скрипт удаляет временного пользователя и добавляет этот заголовок в ответ:
WWW-Authenticate: Basic realm="Password protected"
В этот момент браузер забыл учетные данные Боба.
Мое решение проблемы заключается в следующем. Вы можете найти функцию http_digest_parse
, $realm
а также $users
во втором примере этой страницы: http://php.net/manual/en/features.http-auth.php.
session_start();
function LogOut() {
session_destroy();
session_unset($_SESSION['session_id']);
session_unset($_SESSION['logged']);
header("Location: /", TRUE, 301);
}
function Login(){
global $realm;
if (empty($_SESSION['session_id'])) {
session_regenerate_id();
$_SESSION['session_id'] = session_id();
}
if (!IsAuthenticated()) {
header('HTTP/1.1 401 Unauthorized');
header('WWW-Authenticate: Digest realm="'.$realm.
'",qop="auth",nonce="'.$_SESSION['session_id'].'",opaque="'.md5($realm).'"');
$_SESSION['logged'] = False;
die('Access denied.');
}
$_SESSION['logged'] = True;
}
function IsAuthenticated(){
global $realm;
global $users;
if (empty($_SERVER['PHP_AUTH_DIGEST']))
return False;
// check PHP_AUTH_DIGEST
if (!($data = http_digest_parse($_SERVER['PHP_AUTH_DIGEST'])) ||
!isset($users[$data['username']]))
return False;// invalid username
$A1 = md5($data['username'] . ':' . $realm . ':' . $users[$data['username']]);
$A2 = md5($_SERVER['REQUEST_METHOD'].':'.$data['uri']);
// Give session id instead of data['nonce']
$valid_response = md5($A1.':'.$_SESSION['session_id'].':'.$data['nc'].':'.$data['cnonce'].':'.$data['qop'].':'.$A2);
if ($data['response'] != $valid_response)
return False;
return True;
}
Я нашел единственный эффективный способ уничтожить PHP_AUTH_DIGEST
или же PHP_AUTH_USER
А ТАКЖЕ PHP_AUTH_PW
учетные данные для вызова заголовка HTTP/1.1 401 Unauthorized
,
function clear_admin_access(){
header('HTTP/1.1 401 Unauthorized');
die('Admin access turned off');
}
Как правило, когда браузер запрашивает учетные данные у пользователя и передает их на определенный веб-сайт, он будет продолжать делать это без дальнейших запросов. В отличие от различных способов очистки файлов cookie на стороне клиента, я не знаю аналогичного способа попросить браузер забыть предоставленные учетные данные для аутентификации.
Мне нужно было сбросить авторизацию.htaccess, поэтому я использовал это:
<?php
if (!isset($_SERVER['PHP_AUTH_USER'])) {
header('WWW-Authenticate: Basic realm="My Realm"');
header('HTTP/1.0 401 Unauthorized');
echo 'Text to send if user hits Cancel button';
exit;
}
?>
Нашел здесь: http://php.net/manual/en/features.http-auth.php
Пойди разберись.
На этой странице находится несколько решений, и даже внизу: Lynx, не очищает аутентификацию, как другие браузеры;)
Я протестировал его в установленных браузерах и, закрыв его, кажется, что каждый браузер постоянно требует повторного входа.
Это может быть не то решение, которое искали, но я решил это так. у меня есть 2 сценария для выхода из системы.
logout.php
<?php
header("Location: http://.@domain.com/log.php");
?>
log.php
<?php
header("location: https://google.com");
?>
Таким образом, я не получаю предупреждение, и моя сессия прекращается
По умолчанию Trac использует HTTP-аутентификацию. Выход из системы не работает и не может быть исправлен:
- Это проблема самой схемы HTTP-аутентификации, и мы ничего не можем сделать в Trac, чтобы исправить ее должным образом.
- В настоящее время нет обходного пути (JavaScript или другой), который работает со всеми основными браузерами.
От: http://trac.edgewall.org/ticket/791
Похоже, что на этот вопрос нет рабочего ответа, об этой проблеме было сообщено семь лет назад, и это имеет смысл: HTTP не имеет состояния. Либо запрос выполняется с учетными данными аутентификации, либо нет. Но это вопрос клиента, отправляющего запрос, а не сервера, получающего его. Сервер может только сказать, требует ли авторизация URI запроса или нет.
Может быть, я упускаю суть.
Самый надежный способ завершить HTTP-аутентификацию - закрыть браузер и все окна браузера. Вы можете закрыть окно браузера, используя Javascript, но я не думаю, что вы можете закрыть все окна браузера.
Лучшее решение, которое я нашел до сих пор (это своего рода псевдокод, $isLoggedIn
является псевдопеременной для http auth):
Во время "выхода из системы" просто сохраните некоторую информацию в сеансе о том, что пользователь фактически вышел из системы.
function logout()
{
//$isLoggedIn = false; //This does not work (point of this question)
$_SESSION['logout'] = true;
}
В месте, где я проверяю подлинность, я расширяю условие:
function isLoggedIn()
{
return $isLoggedIn && !$_SESSION['logout'];
}
Сеанс в некоторой степени связан с состоянием проверки подлинности http, поэтому пользователь остается в системе, пока он сохраняет браузер открытым и пока в браузере сохраняется проверка подлинности http.
AFAIK, нет чистого способа реализовать функцию "выхода из системы" при использовании аутентификации htaccess (т.е. на основе HTTP).
Это связано с тем, что такая аутентификация использует код ошибки HTTP "401", чтобы сообщить браузеру, что требуются учетные данные, и в этот момент браузер запрашивает детали у пользователя. С этого момента, пока браузер не будет закрыт, он всегда будет отправлять учетные данные без дополнительных запросов.
В то время как другие правы, говоря, что невозможно выйти из обычной http-аутентификации, существуют способы реализации аутентификации, которые ведут себя аналогично. Одним из очевидных подходов является использование auth_memcookie. Если вы действительно хотите реализовать обычную HTTP-аутентификацию (т.е. использовать диалоги браузера для входа в систему, а не форму HTTP), используя это - просто установите аутентификацию в отдельный защищенный каталог.htaccess, содержащий скрипт PHP, который перенаправляет туда, куда пришел пользователь после создание сессии memcache.
Я резюмировал свое решение в статье ( https://www.hattonwebsolutions.co.uk/articles/how_to_logout_of_http_sessions), однако я использовал вызов ajax и два файла htaccess (как предлагается в этом вопросе: Как выйти из HTTP-аутентификации (htaccess), который работает в Google Chrome?).
Короче - вы:
- Создайте подпапку с файлом htaccess с тем же именем AuthName, но для этого потребуется другой пользователь
- Отправьте запрос ajax на страницу (с неправильным именем пользователя) (что не удается), а затем инициируйте перенаправление тайм-аута на страницу, вышедшую из системы.
Это позволяет избежать появления вторичного всплывающего окна в папке выхода с запросом другого имени пользователя (что могло бы сбить с толку пользователей). В моей статье используется JQuery, но этого можно избежать.
Здесь много отличных - сложных ответов. В моем конкретном случае я нашел чистое и простое решение для выхода из системы. Мне еще предстоит тестировать в Edge. На моей странице, на которой я вошел, я разместил ссылку на выход, подобную этой:
<a href="https://MyDomainHere.net/logout.html">logout</a>
И в начале этой страницы logout.html (которая также защищена.htaccess) у меня есть обновление страницы, похожее на это:
<meta http-equiv="Refresh" content="0; url=https://logout:logout@MyDomainHere.net/" />
Где бы вы оставили слова "выход" на месте, чтобы очистить имя пользователя и пароль, кешированные для сайта.
Я признаю, что если необходимо сразу войти в несколько страниц, то для каждой из этих точек входа потребуется собственная соответствующая страница logout.html. В противном случае вы могли бы централизовать выход из системы, введя в процесс дополнительный шаг привратника перед фактическим запросом входа в систему, требуя ввода фразы для достижения места назначения входа.
Я добился этого, отправив заголовок 401. Это прекрасно работает. С PHP
header('HTTP/1.0 401 Unauthorized');