Выход из аутентификации HTTP через PHP

151

Как правильно выйти из защищенной папки HTTP-аутентификации?

Есть обходные пути, которые могут достигнуть этого, но они потенциально опасны, потому что они могут содержать ошибки или не работать в определенных ситуациях / браузерах. Вот почему я ищу правильное и чистое решение.

Йозеф Сабл
источник
Пожалуйста, укажите цель вашего выхода. Должен ли это быть принудительный выход из системы (деактивация пользователя)? Простая функция выхода из системы для пользователя? Что-нибудь еще?
Карстен
6
Я не понимаю, почему это важно, но это оба случая: деактивация на основе внутренних условий в приложении, а также типичная кнопка выхода из системы. Пожалуйста, объясните, почему это важно, я отредактирую это прямо в вопросе.
Йозеф Сабл
2
«Правильное и чистое решение» - браузеры, имеющие собственную кнопку выхода из системы, которая при нажатии заставит браузер прекратить отправку заголовков аутентификации ... Можно мечтать, верно?
DanMan
1
На панели инструментов веб-разработчика есть такая «кнопка».
Йозеф Сабл
Что сказал Йозеф: панель инструментов веб-разработчика для Firefox ->Miscellaneous -> Clear Private Data -> HTTP Authentication
Ярин

Ответы:

103

Mu. Не существует правильного способа , даже не совместимого в разных браузерах.

Это проблема, вытекающая из спецификации HTTP (раздел 15.6):

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

С другой стороны, в разделе 10.4.2 говорится:

Если в запрос уже включены учетные данные авторизации, то ответ 401 указывает, что в авторизации было отказано для этих учетных данных. Если ответ 401 содержит ту же проблему, что и предыдущий ответ, и пользовательский агент уже предпринял попытку аутентификации по крайней мере один раз, тогда пользователю СЛЕДУЕТ представить объект, который был указан в ответе, поскольку этот объект может включать в себя соответствующую диагностическую информацию.

Другими словами, вы можете снова показать окно входа в систему (как говорит @Karsten ), но браузер не должен выполнять ваш запрос - так что не слишком зависите от этой (неправильной) функции.

Писквор покинул здание
источник
9
Это ошибка в RFC. W3C лень исправлять. Очень грустно.
Эрик Аронесты
Как предложил @Jonathan Hanson ниже , вы можете использовать файлы cookie для отслеживания вместе с HTTP-аутентификацией. Это лучший метод для меня.
Machineaddict
61

Метод, который хорошо работает в Safari. Также работает в Firefox и Opera, но с предупреждением.

Location: http://logout@yourserver.example.com/

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

Корнель
источник
14
Согласно RFC 3986 (URI: Общий синтаксис) раздел 3.2.1. (Информация о пользователе) использование user:password@hostне рекомендуется. Использование только http://logout@yourserver.example.com/не является и должно работать в большинстве случаев.
AEF
1
@andho: да, это редирект. Вы должны использовать его со статусом 302.
Корнель
1
Очевидно, что простая ссылка на logout@yourserver.example.com также работает (ссылка «отключить» на этот URL) вместо http перенаправления в PHP ... есть ли минус в этом?
Моала
4
Остерегайтесь: отправка формы с использованием относительного пути может завершиться ошибкой, если она будет выполнена после повторного входа в систему (войдите в систему с приглашением на выход из системы), поскольку адрес все равно будет logout@yourserver.example.com/path и не yourserver.example.com/path /
Джейсон
1
logout@yourserver.example.com работает без проблем в Chrome, но предлагает запрос безопасности в Firefox. Выход из системы: true@yourserver.example.com не делает Firefox требованием безопасности. Ни один из двух URL не работает в IE8: /
Тор А. Педерсен,
46

Простой ответ заключается в том, что вы не можете надежно выйти из http-аутентификации.

Длинный ответ:
Http-auth (как и остальная часть спецификации HTTP) подразумевает отсутствие состояния. Таким образом, «войти» или «выйти» на самом деле не имеет смысла. Лучший способ убедиться в этом - спросить для каждого HTTP-запроса (и помните, что загрузка страницы обычно состоит из нескольких запросов): «Вам разрешено делать то, что вы запрашиваете?». Сервер видит каждый запрос как новый и не связанный с любыми предыдущими запросами.

Браузеры решили запомнить учетные данные, которые вы сообщаете им на первых 401, и повторно отправлять их без явного разрешения пользователя на последующие запросы. Это попытка дать пользователю ожидаемую модель «вошел / вышел», но это просто помеха. Это браузер, который имитирует эту устойчивость состояния. Веб-сервер совершенно не знает об этом.

Таким образом, «выход из системы» в контексте http-auth является чисто симуляцией, предоставляемой браузером, и поэтому находится вне полномочий сервера.

Да, есть кладжи. Но они нарушают RESTful-ность (если это имеет значение для вас), и они ненадежны.

Если вам абсолютно необходима модель входа в систему / выхода из нее для проверки подлинности вашего сайта, лучшим выбором будет файл cookie для отслеживания с сохранением состояния на сервере каким-либо образом (mysql, sqlite, flatfile и т. Д.). Это потребует оценки всех запросов, например, с помощью PHP.

Джонатан Хансон
источник
26

Временное решение

Вы можете сделать это, используя 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 OK.

Антон Мочалин
источник
5
Это своего рода обходной путь для входа в систему от имени другого пользователя. Но это на самом деле работает и заслуживает большего доверия.
Чарли Руденстол
2
Я думаю, что это лучший ответ. Как указано в этом ответе на подобный вопрос, может быть некоторое преимущество при рандомизации пароля.
Zelanix
2
Это было то, что я хотел - работал во всех браузерах без проблем. Хранил страницу "выхода", которую я унаследовал в целости и сохранности. Я не обязательно хотел использовать JS (возможно, иррационально), но у всех остальных ответов были проблемы с кроссбраузерностью, и это работало отлично.
dgig
Я не могу сделать эту работу так, как объясняется. Когда я вернулся в защищенную область, браузер снова аутентифицирует себя, отправив последние использованные действительные учетные данные в заголовок. Однако с небольшими изменениями это сработало для меня. Я изменил ответ «200 OK» с заголовком с той же Целью защищенной области, но принимающим только пользователя / пароль «logout: logout». Таким образом, пользователь вошел в систему с этим пользователем «выхода из системы», и этот пользователь повторяет попытку, когда он возвращается в защищенную область. Защищенная область отклоняет этого пользователя / пароль, поэтому пользователь может изменить свои учетные данные.
Jonaguera
2
Это не работает, как это объясняется. Протестировано в Chrome 40 и Firefox 35.
funforums
13

Обходной путь (не чистое, хорошее (или даже работающее! См. Комментарии) решение):

Отключите его учетные данные один раз.

Вы можете переместить свою логику 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

Так что отключение его учетных данных один раз должно быть тривиальным.

Карстен
источник
18
Проблема с этим решением заключается в следующем: вы даете IE знать, что учетные данные не в порядке. Он отображает диалог входа в систему с пустыми полями (без отображения значений, сохраненных в менеджере паролей). Но когда вы нажимаете «Отмена» и обновляете страницу, она отправляет сохраненные учетные данные, таким образом, снова входя в систему.
Йозеф Сабл
Downvoted; Как прокомментировал Джозеф Сейбл, проблема не решается.
Крис Весселинг
7

Выйти из HTTP Basic Auth в два этапа

Допустим, у меня есть область HTTP Basic Auth с именем «Защищено паролем», и Боб вошел в систему. Чтобы выйти из системы, я делаю 2 AJAX-запроса:

  1. Сценарий доступа / logout_step1. Он добавляет случайного временного пользователя в .htusers и отвечает его логином и паролем.
  2. Доступ к скрипту / logout_step2, аутентифицированному с помощью временного имени пользователя и пароля . Скрипт удаляет временного пользователя и добавляет этот заголовок в ответ:WWW-Authenticate: Basic realm="Password protected"

В этот момент браузер забыл учетные данные Боба.

Влад ГУРДИГА
источник
1
Вот Это Да! Это действительно заслуживает +1 за чистую изобретательность, даже если это совершенно чокнутая вещь.
Энди Триггс
7

Мое решение проблемы заключается в следующем. Вы можете найти функцию 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;
}
Pie86
источник
4

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

Грег Хьюгилл
источник
Я считаю, что есть возможность удалить аутентифицированные сессии, когда вы выбираете «Удалить личные данные» в Firefox
Кристиан Дж.
1
Также расширение панели инструментов веб-разработчика для Firefox предлагает функцию удаления HTTP-аутентификаций. Но это не
подлежит
2
По умолчанию Firefox выходит из HTTP-аутентификации в разделе «Инструменты»> «Очистить недавнюю историю ...», а также в поле «Активные входы в систему». Это не интуитивно понятно и не позволяет вам выходить только из одного домена, вы всегда выходите из каждой страницы.
AEF
2

По умолчанию Trac использует HTTP-аутентификацию. Выход из системы не работает и не может быть исправлен:

  • Это проблема самой схемы проверки подлинности HTTP, и мы ничего не можем сделать в Trac, чтобы исправить ее должным образом.
  • В настоящее время нет обходного пути (JavaScript или другой), который работает со всеми основными браузерами.

От: http://trac.edgewall.org/ticket/791#comment:103

Похоже, что на этот вопрос нет рабочего ответа, об этой проблеме было сообщено семь лет назад, и это имеет смысл: HTTP не имеет состояния. Либо запрос выполняется с учетными данными аутентификации, либо нет. Но это вопрос клиента, отправляющего запрос, а не сервера, получающего его. Сервер может только сказать, требует ли авторизация URI запроса или нет.

hakre
источник
2

Мне нужно было сбросить авторизацию .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, не очищает аутентификацию, как другие браузеры;)

Я протестировал его в установленных браузерах и, закрыв его, кажется, что каждый браузер постоянно требует повторного входа.

Дули
источник
Кажется, это не работает, я получаю текст отмены без всплывающего окна входа в систему.
Майкл
Оказывается, отправка WWW-Authenticateвызывала проблему, избавляясь от того, что выписал меня автоматически.
Майкл
И наоборот, кажется, что НЕ отправка сообщения во WWW-Authenticateвремя устранения проблемы в одном браузере (Chrome) заставляет другой браузер (Firefox) запоминать учетные данные и отправлять их при следующем запросе, что приводит к автоматическому повторному входу в систему! Argh!
Майкл
Тогда посмотрите на UA и сделайте одно или другое похожим на решение
Леннарт Роллан
2

Это может быть не то решение, которое искали, но я решил это так. У меня есть 2 сценария для выхода из системы.

logout.php

<?php
header("Location: http://.@domain.com/log.php");
?>

log.php

<?php
header("location: https://google.com");
?>

Таким образом, я не получаю предупреждение, и моя сессия прекращается

Kevin
источник
1
Это было единственное решение, которое действительно сработало для меня! Проверено на Firefox 37 и Chromium 41
zesaver
1

AFAIK, нет чистого способа реализовать функцию «выхода из системы» при использовании аутентификации htaccess (т.е. на основе HTTP).

Это связано с тем, что такая аутентификация использует код ошибки HTTP «401», чтобы сообщить браузеру, что требуются учетные данные, после чего браузер запрашивает у пользователя подробности. С этого момента, пока браузер не будет закрыт, он всегда будет отправлять учетные данные без дополнительных запросов.

Альнитак
источник
1

Лучшее решение, которое я нашел до сих пор (это своего рода псевдокод, $isLoggedIn псевдопеременная is для 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.

Йозеф Сабл
источник
4
Хотя базовая аутентификация http является RESTful, сеансы - нет.
Деймон
1

Может быть, я упускаю суть.

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

Тоби аллен
источник
К сожалению, некоторые браузеры не закрывают окно, если открыта только одна вкладка, поэтому вопрос действительно спорный
scape
Много лет назад у меня была задача реализовать кнопку выхода из системы, не закрывая окно :-) Но, возможно, они не остановились бы на том, чтобы «не закрывать окно». Но, эй, это простое решение, которое может сработать для кого-то, и я, если честно, пропустил его тогда.
Йозеф Сабл
1

Единственный эффективный способ стереть учетные данные PHP_AUTH_DIGESTили PHP_AUTH_USERAND PHP_AUTH_PW- это вызвать заголовок HTTP/1.1 401 Unauthorized.

function clear_admin_access(){
    header('HTTP/1.1 401 Unauthorized');
    die('Admin access turned off');
}
КРУГ
источник
0

В то время как другие правы, говоря, что невозможно выйти из обычной http-аутентификации, существуют способы реализации аутентификации, которые ведут себя аналогично. Одним из очевидных подходов является использование auth_memcookie . Если вы действительно хотите реализовать базовую HTTP-аутентификацию (т.е. использовать диалоги браузера для входа в систему, а не форму HTTP), используя это - просто установите аутентификацию в отдельный защищенный каталог .htaccess, содержащий скрипт PHP, который перенаправляет назад туда, куда пришел пользователь создание сессии memcache.

symcbean
источник
0

Здесь много отличных - сложных ответов. В моем конкретном случае я нашел чистое и простое решение для выхода из системы. Мне еще предстоит тестировать в 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. В противном случае вы могли бы централизовать выход из системы, введя в процесс дополнительный шаг привратника перед фактическим запросом входа в систему, требуя ввода фразы для достижения места назначения входа.

Джон Уэйн
источник
1
при движении вперед это работает, он выходит из системы, но история возврата браузера может восстановить сеанс.
Джонвейн