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


151

Який правильний спосіб вийти із захищеної папки аутентифікації HTTP?

Існують способи вирішення, які можуть цього досягти, але вони потенційно небезпечні, оскільки можуть бути помилковими або не працювати в певних ситуаціях / браузерах. Ось чому я шукаю правильне та чисте рішення.


Будь ласка, вкажіть мету вашого виходу. Чи повинен це бути вимушений вихід (деактивація користувача)? Проста функція виходу для користувача? Щось іще?
Карстен

6
Я не розумію, чому це має значення, але це обидва випадки: дезактивація на основі внутрішніх умов у застосуванні, а також типова кнопка виходу. Поясніть, будь ласка, чому це важливо, я відредагую це безпосередньо у запитанні.
Йозеф Сабл

2
"Правильне та чисте рішення" - це веб-переглядачі, які мають власну кнопку виходу, яка при натисканні на нього змусить браузер перестати надсилати заголовки Auth ... Можна мріяти, правда?
DanMan

1
Панель інструментів веб-розробників має таку "кнопку".
Йозеф Сабл

Що Йозеф сказав: панель інструментів веб-розробників для Firefox ->Miscellaneous -> Clear Private Data -> HTTP Authentication
Ярін

Відповіді:


103

Му. Не існує жодного правильного способу , навіть такого, який відповідає веб-переглядачам.

Це проблема, що виникає із специфікації HTTP (розділ 15.6):

Існуючі HTTP-клієнти та користувацькі агенти зазвичай зберігають інформацію про автентифікацію на невизначений термін. HTTP / 1.1. не забезпечує сервер методом спрямування клієнтів для відмови від цих кешованих даних.

З іншого боку, розділ 10.4.2 говорить:

Якщо запит вже включав в себе дані авторизації, відповідь 401 вказує на те, що авторизація відхилена для цих даних. Якщо відповідь 401 містить такий самий виклик, що і попередня відповідь, і користувальницький агент вже намагався перевірити автентифікацію хоча б один раз, тоді користувачеві ПОТРІБНО представити сутність, яку було надано у відповіді, оскільки ця сутність може містити відповідну діагностичну інформацію.

Іншими словами, ви, можливо, зможете знову показати поле для входу (як говорить @Karsten ), але браузер не повинен шанувати ваш запит - тому не надто залежно від цієї (неправильної) функції.


9
Це помилка в RFC. W3C занадто ліниво виправити. Так сумно.
Ерік Аронесті

Як запропонував @Jonathan Hanson нижче , ви можете використовувати відстежуючий cookie разом із HTTP-аутентифікацією. Це найкращий метод для мене.
машинобудівний вирок

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-ness (якщо це для вас цінно) і вони ненадійні.

Якщо вам абсолютно потрібна модель для входу / виходу з системи для аутентифікації вашого сайту, найкраща ставка - це відстежуючий файл 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 ОК до цього запиту (тобто він не повинен вимагати аутентифікації HTTP).

Замініть '/where/to/redirect'деякий шлях для переадресації після виходу з системи та замініть '/path/that/will/return/200/OK'деякий шлях на вашому сайті, який поверне 200 ОК.


5
Це трохи вирішення проблеми для входу в систему як іншого користувача. Але це насправді працює і заслуговує на більший кредит.
Charlie Rudenstål

2
Я думаю, що це найкраща відповідь. Як зазначено у цій відповіді на подібне запитання, може бути певна перевага для рандомізації пароля.
zelanix

2
Це було те, що я хотів - працював у всіх браузерах без проблем. Зберігав сторінку "вихід", яку я успадкував недоторканою. Мені не обов'язково хотілося використовувати JS (можливо, нераціонально), але в інших відповідях усі проблеми мали перехресний веб-браузер, і це спрацювало ідеально.
дгіг

Я не можу зробити цю роботу так, як це пояснюється. Коли я повернувся до захищеної області, веб-переглядач знову автентифікує себе, надсилаючи в заголовок останні використані дійсні дані. Однак, трохи змінившись, це спрацювало для мене. Я змінив відповідь 200 ОК із заголовком із тим самим царством захищеної області, але прийняв лише користувача "пропуск: вихід". Таким чином, користувач увійшов в систему з цим користувачем "вихід", і це той користувач, який повторно намагається повернутися до захищеної зони. Захищена зона відхиляє цього користувача / пропуск, щоб користувач міг змінити свої облікові дані.
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 знати, що облікові дані не в порядку. У ньому відображається діалогове вікно входу з порожніми полями (не відображаються значення, що зберігаються в диспетчері паролів). Але коли ви натискаєте скасувати та оновити сторінку, вона надсилає збережені облікові дані, таким чином знову входити в систему.
Йозеф Сабл

Захищений; Як коментує Йозеф Собл, це не вирішує проблему.
Кріс Вессілінг

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;
}

4

Як правило, після того, як браузер запитає у користувача облікові дані та надав їх на певний веб-сайт, він буде продовжувати це робити без додаткового підказки. На відміну від різних способів очищення файлів cookie на стороні клієнта, я не знаю подібного способу попросити браузер забути надані йому автентифікаційні дані.


Я вважаю, що у Firefox виберіть "Видалити приватні дані", можливо, видалити аутентифіковані сесії
Крістіан Дж.

1
Також розширення Панелі інструментів веб-розробників для Firefox пропонує функцію видалення автентифікацій HTTP. Але це не викликає сумнівів, оскільки ми дійсно не можемо попросити наших користувачів завантажити розширення FF або запустити
загадочні

2
За замовчуванням Firefox спосіб виходу з HTTP auth доступний у розділі "Інструменти"> "Очистити недавню історію ...", як прапорець "Активні входи". Це не є інтуїтивно зрозумілим і не дозволяє вам виходити лише з одного домену, ви завжди виходите з кожної сторінки.
aef

2

Trac - за замовчуванням також використовує HTTP-аутентифікацію. Вихід не працює і його неможливо виправити:

  • Це проблема з самою схемою аутентифікації HTTP, і ми не можемо зробити нічого в Trac, щоб правильно її виправити.
  • Наразі не існує способу вирішення (JavaScript чи іншого), який би працював із усіма основними браузерами.

Від: http://trac.edgewall.org/ticket/791#comment:103

Схоже, що немає жодної робочої відповіді на питання, про це питання повідомлялося сім років тому, і це має ідеальний сенс: HTTP є без громадянства. Або запит робиться з використанням облікових даних аутентифікації, або ні. Але це справа клієнта, який надсилає запит, а не сервера, який його отримує. Сервер може сказати, лише якщо URI запиту потребує авторизації чи ні.


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) запам'ятати облікові дані та відправити їх на наступний запит, що призводить до автоматичного повторного входу! Арг!
Майкл

Тоді подивитись на UA і робити те чи інше, здається, рішення
Леннарт Ролланд

2

Це може бути не рішення, яке шукали, але я вирішив це так. У мене є 2 сценарії для процесу виходу.

logout.php

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

log.php

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

Таким чином я не отримую попередження, і мій сеанс припиняється


1
Це єдине рішення, яке справді працювало для мене! Тестували на Firefox 37 та Chromium 41
zesaver

1

AFAIK, немає чіткого способу реалізувати функцію "вихід" при використанні аутентифікації htaccess (тобто на основі HTTP).

Це пояснюється тим, що для такої автентифікації використовується код помилки HTTP '401', щоб повідомити браузеру, що потрібні облікові дані, і в цей момент браузер запитує користувача на деталі. З цього моменту, поки браузер не закриється, він завжди надсилатиме облікові дані, не вимагаючи додаткових запитів.


1

Найкраще рішення, яке я знайшов до цього часу (це псевдокод, $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 зберігається у веб-переглядачі.


4
У той час як http основна автентифікація є RESTful, сеансів немає.
Діамон

1

Можливо, я пропускаю суть.

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


fyi деякі веб-переглядачі не закриють вікно, якщо це відкрита єдина вкладка, тому справа справді
спірна

Ті довгі роки тому у мене було завдання реалізувати кнопку виходу, не закриваючи вікно :-) Але, можливо, вони не зупиняться на "не закриваю вікно". Але ей, це просте рішення, яке може комусь спрацювати, і я тоді його пропустив, якщо чесно.
Йозеф Сабл

1

Єдиний ефективний спосіб я викреслити облікові дані 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');
}

0

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


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
коли рухається вперед, це працює, він виходить з системи, але історія повернення браузера все ще може відновити сеанс.
johnwayne
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.