Abmelden der HTTP-Authentifizierung über PHP

151

Was ist der richtige Weg, um sich von einem durch HTTP-Authentifizierung geschützten Ordner abzumelden?

Es gibt Problemumgehungen, die dies erreichen können, aber sie sind möglicherweise gefährlich, da sie fehlerhaft sein können oder in bestimmten Situationen / Browsern nicht funktionieren. Deshalb suche ich nach einer richtigen und sauberen Lösung.

Josef Sábl
quelle
Bitte geben Sie den Zweck für Ihre Abmeldung an. Sollte dies eine erzwungene Abmeldung sein (Benutzer-Deaktivierung)? Einfache Abmeldefunktion für den Benutzer? Noch etwas?
Karsten
6
Ich verstehe nicht, warum dies wichtig ist, aber es sind beide Fälle: Deaktivierung basierend auf internen Bedingungen in der Anwendung sowie typische Abmeldeschaltfläche. Bitte erklären Sie, warum es wichtig ist, ich werde es direkt in die Frage bearbeiten.
Josef Sábl
2
Die "richtige und saubere Lösung" wären Browser mit einer eigenen Abmeldeschaltfläche, die beim Klicken dazu führt, dass der Browser die Auth-Header nicht mehr sendet ... Man kann träumen, oder?
DanMan
1
Die Web Developer Toolbar verfügt über eine solche "Schaltfläche".
Josef Sábl
Was Josef gesagt hat: Webentwickler-Symbolleiste für Firefox ->Miscellaneous -> Clear Private Data -> HTTP Authentication
Yarin

Antworten:

103

Mu. Es gibt keinen richtigen Weg , nicht einmal einen, der über alle Browser hinweg konsistent ist.

Dies ist ein Problem, das aus der HTTP-Spezifikation (Abschnitt 15.6) stammt:

Bestehende HTTP-Clients und Benutzeragenten behalten Authentifizierungsinformationen normalerweise auf unbestimmte Zeit bei. HTTP / 1.1. bietet keine Methode für einen Server, um Clients anzuweisen, diese zwischengespeicherten Anmeldeinformationen zu verwerfen.

Auf der anderen Seite heißt es in Abschnitt 10.4.2 :

Wenn die Anforderung bereits Berechtigungsnachweise enthielt, zeigt die Antwort 401 an, dass die Berechtigung für diese Anmeldeinformationen verweigert wurde. Wenn die 401-Antwort dieselbe Herausforderung wie die vorherige Antwort enthält und der Benutzeragent bereits mindestens einmal versucht hat, sich zu authentifizieren, MUSS dem Benutzer die Entität angezeigt werden, die in der Antwort angegeben wurde, da diese Entität möglicherweise relevante Diagnoseinformationen enthält.

Mit anderen Worten, Sie können das Anmeldefeld möglicherweise erneut anzeigen (wie @Karsten sagt), aber der Browser muss Ihrer Anfrage nicht nachkommen. Verlassen Sie sich also nicht zu sehr auf diese (falsche) Funktion.

Piskvor verließ das Gebäude
quelle
9
Dies ist ein Fehler im RFC. W3C zu faul, um es zu reparieren. So traurig.
Erik Aronesty
Wie unten von @Jonathan Hanson vorgeschlagen , können Sie zusammen mit der HTTP-Authentifizierung ein Tracking-Cookie verwenden. Dies ist die beste Methode für mich.
Machineaddict
61

Methode, die in Safari gut funktioniert. Funktioniert auch in Firefox und Opera, jedoch mit einer Warnung.

Location: http://[email protected]/

Dadurch wird der Browser angewiesen, die URL mit dem neuen Benutzernamen zu öffnen und den vorherigen zu überschreiben.

Kornel
quelle
14
Gemäß RFC 3986 (URI: Generic Syntax) Abschnitt 3.2.1. (Benutzerinformationen) Die Verwendung von user:password@hostist veraltet. Nur zu verwenden http://[email protected]/ist und sollte in den meisten Fällen nicht funktionieren.
Aef
1
@andho: Ja, es ist eine Weiterleitung. Sie sollten es mit dem Status 302 verwenden.
Kornel
1
Anscheinend funktioniert auch ein einfacher Link zu [email protected] (ein "Trennen" -Link zu dieser URL) anstelle einer http-Weiterleitung in PHP ... irgendein Nachteil?
Moala
4
Achtung: Die Formularübermittlung unter Verwendung des relativen Pfads schlägt möglicherweise fehl, wenn sie nach einer erneuten Anmeldung (Anmeldung mit der Abmeldeaufforderung) erfolgt, da die Adresse weiterhin [email protected]/path und nicht yourserver.example.com/path lautet /
Jason
1
[email protected] funktioniert ohne Probleme in Chrome, fordert jedoch in Firefox zu einer Sicherheitsfrage auf. Abmelden: [email protected] macht Firefox nicht zu einer Sicherheitsfrage. Keine der beiden URLs funktioniert in IE8: /
Thor A. Pedersen
46

Die einfache Antwort lautet, dass Sie sich nicht zuverlässig von der http-Authentifizierung abmelden können.

Die lange Antwort:
Http-auth (wie der Rest der HTTP-Spezifikation) soll zustandslos sein. "Angemeldet" oder "Abgemeldet" zu sein, ist also kein wirklich sinnvolles Konzept. Der bessere Weg, dies zu sehen, besteht darin, für jede HTTP-Anforderung (und denken Sie daran, dass beim Laden einer Seite normalerweise mehrere Anforderungen erforderlich sind) zu fragen, ob Sie das tun dürfen, was Sie anfordern. Der Server sieht jede Anforderung als neu an und hat nichts mit früheren Anforderungen zu tun.

Browser haben sich entschieden, sich die Anmeldeinformationen zu merken, die Sie ihnen beim ersten 401 mitgeteilt haben, und sie ohne die ausdrückliche Erlaubnis des Benutzers bei nachfolgenden Anforderungen erneut zu senden. Dies ist ein Versuch, dem Benutzer das erwartete Modell "angemeldet / abgemeldet" zu geben, aber es ist nur ein Kludge. Es ist der Browser , der diese Beständigkeit des Zustands simuliert. Der Webserver ist sich dessen überhaupt nicht bewusst.

Das "Abmelden" im Kontext von http-auth ist also eine reine Simulation, die vom Browser bereitgestellt wird und somit außerhalb der Autorität des Servers liegt.

Ja, es gibt Kludges. Aber sie brechen die Ruhe (wenn das für Sie von Wert ist) und sie sind unzuverlässig.

Wenn Sie für Ihre Site-Authentifizierung unbedingt ein angemeldetes / abgemeldetes Modell benötigen, ist die beste Wahl ein Tracking-Cookie, bei dem der Status auf irgendeine Weise auf dem Server gespeichert bleibt (MySQL, SQLite, Flatfile usw.). Dies erfordert, dass alle Anforderungen beispielsweise mit PHP ausgewertet werden.

Jonathan Hanson
quelle
26

Problemumgehung

Sie können dies mit Javascript tun:

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

Was oben gemacht wird ist:

  • für IE - einfach den Auth-Cache leeren und irgendwo umleiten

  • Für andere Browser : Senden Sie eine XMLHttpRequest hinter den Kulissen mit dem Anmeldenamen und dem Kennwort "Abmelden". Wir müssen es an einen Pfad senden, der 200 OK für diese Anforderung zurückgibt (dh es sollte keine HTTP-Authentifizierung erforderlich sein).

Durch '/where/to/redirect'einen Pfad ersetzen, zu dem nach dem Abmelden umgeleitet werden soll, und durch '/path/that/will/return/200/OK'einen Pfad auf Ihrer Site ersetzen , der 200 OK zurückgibt.

Anton Mochalin
quelle
5
Es ist eine Art Problemumgehung, sich als anderer Benutzer anzumelden. Aber das funktioniert tatsächlich und verdient mehr Anerkennung.
Charlie Rudenstål
2
Ich denke, dass dies die beste Antwort ist. Wie in dieser Antwort auf eine ähnliche Frage angegeben, kann die zufällige Auswahl des Kennworts von Vorteil sein.
Zelanix
2
Dies war, was ich wollte - funktionierte in allen Browsern ohne Probleme. Hielt die "Abmeldeseite", die ich geerbt habe, intakt. Ich wollte JS nicht unbedingt verwenden (vielleicht irrational), aber die anderen Antworten hatten alle browserübergreifende Probleme und dies funktionierte perfekt.
Dgig
Ich kann diese Arbeit nicht so machen, wie es erklärt wird. Wenn ich zum gesicherten Bereich zurückgekehrt bin, authentifiziert sich der Browser erneut mit dem Senden der zuletzt verwendeten gültigen Anmeldeinformationen im Header. Mit einer kleinen Änderung funktionierte es jedoch für mich. Ich habe die 200 OK-Antwort mit einem Header mit demselben Bereich des gesicherten Bereichs geändert, aber nur einen Benutzer / Pass "Abmelden: Abmelden" akzeptiert. Auf diese Weise hat sich der Benutzer mit diesem "Abmelde" -Benutzer angemeldet, und dies ist der Benutzer, der es erneut versucht, wenn er zum gesicherten Bereich zurückkehrt. Der gesicherte Bereich lehnt diesen Benutzer / Pass ab, sodass der Benutzer seine Anmeldeinformationen ändern kann.
Jonaguera
2
Dies funktioniert nicht wie erklärt. Getestet in Chrome 40 und Firefox 35.
Funforums
13

Problemumgehung (keine saubere, nette (oder sogar funktionierende! Siehe Kommentare) Lösung):

Deaktivieren Sie seine Anmeldeinformationen einmal.

Sie können Ihre HTTP-Authentifizierungslogik nach PHP verschieben, indem Sie die entsprechenden Header senden (falls nicht angemeldet):

Header('WWW-Authenticate: Basic realm="protected area"');
Header('HTTP/1.0 401 Unauthorized');

Und analysieren Sie die Eingabe mit:

$_SERVER['PHP_AUTH_USER'] // httpauth-user
$_SERVER['PHP_AUTH_PW']   // httpauth-password

Das einmalige Deaktivieren seiner Anmeldeinformationen sollte daher trivial sein.

Karsten
quelle
18
Das Problem bei dieser Lösung ist Folgendes: Sie lassen den IE wissen, dass die Anmeldeinformationen nicht in Ordnung sind. Es zeigt den Anmeldedialog mit leeren Feldern an (ohne die im Passwort-Manager gespeicherten Werte). Wenn Sie jedoch auf Abbrechen klicken und die Seite aktualisieren, werden gespeicherte Anmeldeinformationen gesendet, sodass Sie sich erneut anmelden.
Josef Sábl
Abgestimmt; Wie Josef Sable kommentierte, löst dies das vorliegende Problem nicht.
Chris Wesseling
7

Melden Sie sich in zwei Schritten von HTTP Basic Auth ab

Angenommen, ich habe einen HTTP-Basisauthentifizierungsbereich mit dem Namen "Kennwortgeschützt" und Bob ist angemeldet. Um mich abzumelden, stelle ich zwei AJAX-Anforderungen:

  1. Greifen Sie auf script / logout_step1 zu. Es fügt .htusers einen zufälligen temporären Benutzer hinzu und antwortet mit seinem Login und Passwort.
  2. Greifen Sie auf script / logout_step2 zu, das mit dem Login und dem Passwort des temporären Benutzers authentifiziert ist . Das Skript löscht den temporären Benutzer und fügt diesen Header der Antwort hinzu:WWW-Authenticate: Basic realm="Password protected"

Zu diesem Zeitpunkt vergaß der Browser Bobs Anmeldeinformationen.

Vlad GURDIGA
quelle
1
Beeindruckend! Dies verdient wirklich eine +1 für reinen Erfindungsreichtum, auch wenn es eine völlig verrückte Sache ist.
Andy Triggs
7

Meine Lösung für das Problem ist die folgende. Sie können die Funktion finden http_digest_parse, $realmund $usersim zweiten Beispiel dieser Seite: 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
quelle
4

Sobald ein Browser den Benutzer nach Anmeldeinformationen gefragt und diese an eine bestimmte Website übermittelt hat, wird dies in der Regel ohne weitere Aufforderung fortgesetzt. Im Gegensatz zu den verschiedenen Möglichkeiten, wie Sie Cookies auf der Clientseite löschen können, gibt es keine ähnliche Möglichkeit, den Browser aufzufordern, die angegebenen Authentifizierungsdaten zu vergessen.

Greg Hewgill
quelle
Ich glaube, es gibt eine Option zum Löschen authentifizierter Sitzungen, wenn Sie in Firefox
Kristian J.
1
Auch die Web Developer Toolbar-Erweiterung für Firefox bietet Funktionen zum Löschen von HTTP-Authentifizierungen. Dies kommt jedoch nicht in Frage, da wir unsere Benutzer wirklich nicht bitten können, FF-Erweiterungen herunterzuladen oder kryptische
Browserbefehle
2
Die Standardmethode von Firefox zum Abmelden von der HTTP-Authentifizierung ist unter "Extras"> "Zuletzt verwendeten Verlauf löschen ..." als Kontrollkästchen "Aktive Anmeldungen" verfügbar. Dies ist weder intuitiv noch erlaubt es Ihnen, sich nur von einer Domain abzumelden, Sie melden sich immer von jeder Seite ab.
Aef
2

Trac verwendet standardmäßig auch die HTTP-Authentifizierung. Abmelden funktioniert nicht und kann nicht behoben werden:

  • Dies ist ein Problem mit dem HTTP-Authentifizierungsschema selbst, und in Trac können wir nichts tun, um es ordnungsgemäß zu beheben.
  • Derzeit gibt es keine Problemumgehung (JavaScript oder andere), die mit allen gängigen Browsern funktioniert.

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

Es sieht so aus, als gäbe es keine funktionierende Antwort auf die Frage. Dieses Problem wurde vor sieben Jahren gemeldet und es ist absolut sinnvoll: HTTP ist zustandslos. Entweder wird eine Anfrage mit Authentifizierungsdaten gestellt oder nicht. Aber das ist eine Frage des Clients, der die Anfrage sendet, nicht des Servers, der sie empfängt. Der Server kann nur sagen, ob ein Anforderungs-URI autorisiert werden muss oder nicht.

hakre
quelle
2

Ich musste die .htaccess-Autorisierung zurücksetzen, also habe ich Folgendes verwendet:

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

Fand es hier: http://php.net/manual/en/features.http-auth.php

Stelle dir das vor.

Auf dieser Seite befinden sich eine Reihe von Lösungen, die unten sogar vermerkt sind: Lynx löscht die Authentifizierung nicht wie andere Browser;)

Ich habe es in meinen installierten Browsern getestet und nach dem Schließen scheint jeder Browser beim Wiedereintritt immer wieder neu zu sein.

Dooley
quelle
Dies scheint nicht zu funktionieren. Ich erhalte den Abbruchtext ohne Popup-Anmeldefeld.
Michael
Es stellte sich heraus, dass das Senden des WWW-Authenticatedas Problem verursachte und mich automatisch abmeldete.
Michael
Und umgekehrt scheint es, dass das NICHT- Senden WWW-Authenticatewährend der Behebung des Problems in einem Browser (Chrome) dazu führt, dass sich ein anderer Browser (Firefox) die Anmeldeinformationen merkt und sie bei der nächsten Anfrage sendet, was zu einer automatischen erneuten Anmeldung führt! Argh!
Michael
Dann schauen Sie sich UA an und tun Sie das eine oder andere scheint eine Lösung zu sein
Lennart Rolland
2

Dies ist möglicherweise nicht die gesuchte Lösung, aber ich habe sie so gelöst. Ich habe 2 Skripte für den Abmeldevorgang.

logout.php

<?php
header("Location: http://[email protected]/log.php");
?>

log.php

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

Auf diese Weise erhalte ich keine Warnung und meine Sitzung wird beendet

Kevin
quelle
1
Dies war die einzige Lösung, die wirklich für mich funktioniert hat! Getestet auf Firefox 37 und Chrom 41
Zesaver
1

AFAIK, es gibt keine saubere Möglichkeit, eine "Abmelde" -Funktion zu implementieren, wenn die htaccess-Authentifizierung (dh HTTP-basierte Authentifizierung) verwendet wird.

Dies liegt daran, dass bei einer solchen Authentifizierung der Browser mithilfe des HTTP-Fehlercodes '401' darüber informiert wird, dass Anmeldeinformationen erforderlich sind. Zu diesem Zeitpunkt fordert der Browser den Benutzer zur Eingabe der Details auf. Von da an sendet der Browser bis zum Schließen immer die Anmeldeinformationen ohne weitere Aufforderung.

Alnitak
quelle
1

Die beste Lösung, die ich bisher gefunden habe, ist (es ist eine Art Pseudocode, das $isLoggedInist eine Pseudovariable für http auth):

Zum Zeitpunkt der "Abmeldung" speichern Sie einfach einige Informationen in der Sitzung, die besagen, dass der Benutzer tatsächlich abgemeldet ist.

function logout()
{
  //$isLoggedIn = false; //This does not work (point of this question)
  $_SESSION['logout'] = true;
}

An der Stelle, an der ich nach Authentifizierung suche, erweitere ich die Bedingung:

function isLoggedIn()
{
  return $isLoggedIn && !$_SESSION['logout'];
}

Die Sitzung ist in gewisser Weise mit dem Status der http-Authentifizierung verknüpft, sodass der Benutzer abgemeldet bleibt, solange er den Browser geöffnet hält und die http-Authentifizierung im Browser bestehen bleibt.

Josef Sábl
quelle
4
Während die http-Basisauthentifizierung RESTful ist, sind Sitzungen dies nicht.
Deamon
1

Vielleicht verpasse ich den Punkt.

Der zuverlässigste Weg, die HTTP-Authentifizierung zu beenden, besteht darin, den Browser und alle Browserfenster zu schließen. Sie können ein Browserfenster mit Javascript schließen, aber ich glaube nicht, dass Sie alle Browserfenster schließen können.

Toby Allen
quelle
Zu Ihrer Information, einige Browser schließen ein Fenster nicht, wenn es der einzige geöffnete Tab ist. Der Punkt ist also wirklich umstritten
Scape
Vor diesen langen Jahren hatte ich die Aufgabe, die Abmeldeschaltfläche zu implementieren, ohne das Fenster zu schließen :-) Aber vielleicht würden sie nicht darüber nachdenken, "das Fenster nicht zu schließen". Aber hey, das ist eine einfache Lösung, die für jemanden funktionieren könnte, und ich habe sie damals verpasst, um ehrlich zu sein.
Josef Sábl
1

Die einzige effektive Möglichkeit, die PHP_AUTH_DIGESToder PHP_AUTH_USERAND- PHP_AUTH_PWAnmeldeinformationen zu löschen, besteht darin, den Header aufzurufen HTTP/1.1 401 Unauthorized.

function clear_admin_access(){
    header('HTTP/1.1 401 Unauthorized');
    die('Admin access turned off');
}
KREIS
quelle
0

Während die anderen zu Recht sagen, dass es unmöglich ist, sich von der grundlegenden http-Authentifizierung abzumelden, gibt es Möglichkeiten, eine Authentifizierung zu implementieren, die sich ähnlich verhält . Ein naheliegender Ansatz ist die Verwendung von auth_memcookie . Wenn Sie die grundlegende HTTP-Authentifizierung wirklich implementieren möchten (dh die Browser-Dialoge zum Anmelden verwenden möchten, anstatt ein HTTP-Formular zu verwenden), setzen Sie die Authentifizierung einfach auf ein separates .htaccess-geschütztes Verzeichnis, das ein PHP-Skript enthält, das zurückleitet, wohin der Benutzer gekommen ist Erstellen der Memcache-Sitzung.

Symcbean
quelle
0

Hier gibt es viele großartige - komplexe - Antworten. In meinem speziellen Fall habe ich eine saubere und einfache Lösung für das Abmelden gefunden. Ich muss noch in Edge testen. Auf meiner Seite, auf der ich mich angemeldet habe, habe ich einen ähnlichen Abmeldelink eingefügt:

<a href="https://MyDomainHere.net/logout.html">logout</a>

Und im Kopf dieser logout.html-Seite (die auch durch .htaccess geschützt ist) habe ich eine ähnliche Seitenaktualisierung:

<meta http-equiv="Refresh" content="0; url=https://logout:[email protected]/" />

Wo Sie die Wörter "Abmelden" beibehalten würden, um den für die Site zwischengespeicherten Benutzernamen und das Kennwort zu löschen.

Ich gebe zu, dass, wenn mehrere Seiten von Anfang an direkt angemeldet werden müssen, jeder dieser Einstiegspunkte eine eigene entsprechende logout.html-Seite benötigt. Andernfalls können Sie die Abmeldung zentralisieren, indem Sie vor der eigentlichen Anmeldeaufforderung einen zusätzlichen Gatekeeper-Schritt in den Prozess einfügen, bei dem eine Phrase eingegeben werden muss, um ein Anmeldeziel zu erreichen.

John Wayne
quelle
1
Wenn Sie fortfahren, funktioniert dies, es wird zwar abgemeldet, aber der Browser-Back-Verlauf kann die Sitzung trotzdem wiederherstellen.
Johnwayne