Logout di autenticazione HTTP tramite PHP


151

Qual è il modo corretto per disconnettersi dalla cartella protetta dall'autenticazione HTTP?

Esistono soluzioni alternative che possono raggiungere questo obiettivo, ma sono potenzialmente pericolose perché possono essere difettose o non funzionare in determinate situazioni / browser. Ecco perché sto cercando una soluzione corretta e pulita.


Specificare lo scopo del logout. Questo dovrebbe essere un logout forzato (disattivazione dell'utente)? Semplice funzione di logout per l'utente? Qualunque altra cosa?
Karsten,

6
Non capisco perché sia ​​importante, ma sono entrambi i casi: disattivazione in base alle condizioni interne dell'applicazione e al pulsante di disconnessione tipico. Spiega perché è importante, lo modificherò direttamente nella domanda.
Josef Sábl,

2
La "soluzione corretta e pulita" sarebbero i browser dotati del proprio pulsante di disconnessione che, se cliccato, impedirà al browser di inviare le intestazioni Auth ... Si può sognare, giusto?
DanMan,

1
La barra degli strumenti per sviluppatori Web ha tale "pulsante".
Josef Sábl,

Cosa ha detto Josef: barra degli strumenti per sviluppatori web per Firefox ->Miscellaneous -> Clear Private Data -> HTTP Authentication
Yarin

Risposte:


103

Mu. Non esiste un modo corretto , nemmeno uno coerente tra i browser.

Questo è un problema che deriva dalla specifica HTTP (sezione 15.6):

I client HTTP e gli user agent esistenti in genere conservano le informazioni di autenticazione a tempo indeterminato. HTTP / 1.1. non fornisce un metodo per un server per indirizzare i client a scartare queste credenziali memorizzate nella cache.

D'altra parte, la sezione 10.4.2 dice:

Se la richiesta includeva già le credenziali di autorizzazione, la risposta 401 indica che l'autorizzazione è stata rifiutata per tali credenziali. Se la risposta 401 contiene la stessa sfida della risposta precedente e l'agente utente ha già tentato l'autenticazione almeno una volta, l'utente DOVREBBE essere presentato all'entità che è stata data nella risposta, poiché tale entità potrebbe includere informazioni diagnostiche pertinenti.

In altre parole, potresti essere in grado di mostrare di nuovo la casella di accesso (come dice @Karsten ), ma il browser non deve rispettare la tua richiesta , quindi non dipendere troppo da questa (mis) funzionalità.


9
Questo è un bug nell'RFC. W3C troppo pigro per risolvere. Così triste.
Erik Aronesty,

Come suggerito da @Jonathan Hanson di seguito , è possibile utilizzare un cookie di tracciamento insieme all'autenticazione HTTP. Questo è il metodo migliore per me.
machineaddict,

61

Metodo che funziona bene in Safari. Funziona anche con Firefox e Opera, ma con un avviso.

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

Questo dice al browser di aprire l'URL con un nuovo nome utente, sovrascrivendo quello precedente.


14
Secondo RFC 3986 (URI: sintassi generica) sezione 3.2.1. (Informazioni utente) l'uso di user:password@hostè deprecato. L'uso di solo http://logout@yourserver.example.com/non è e dovrebbe funzionare nella maggior parte dei casi.
aef

1
@andho: sì, è un reindirizzamento. Dovresti usarlo con lo stato 302.
Kornel,

1
Apparentemente funziona anche un semplice link a logout@yourserver.example.com (un link "disconnect" a questo URL) invece di un reindirizzamento http in PHP ... qualche svantaggio?
Moala,

4
Attenzione: presentazione Modulo utilizzando percorso relativo potrebbe non riuscire quando è fatto dopo una ri-accesso (login con la disconnessione rapida), in quanto l'indirizzo sarebbe ancora logout@yourserver.example.com/path e non yourserver.example.com/path /
Jason,

1
logout@yourserver.example.com funziona senza problemi in Chrome, ma richiede una domanda di sicurezza in Firefox. logout: true@yourserver.example.com la dose non rende Firefox una richiesta di sicurezza. Nessuno dei due URL funziona in IE8: /
Thor A. Pedersen il

46

La semplice risposta è che non è possibile disconnettersi in modo affidabile dall'autenticazione http.

La risposta lunga:
Http-auth (come il resto delle specifiche HTTP) è pensato per essere senza stato. Quindi essere "loggato" o "disconnesso" non è davvero un concetto che ha senso. Il modo migliore per vederlo è chiedere, per ogni richiesta HTTP (e ricordare che un caricamento di pagina è di solito più richieste), "ti è permesso fare quello che stai richiedendo?". Il server vede ogni richiesta come nuova e non correlata a alcuna richiesta precedente.

I browser hanno scelto di ricordare le credenziali comunicate nel primo 401 e di inviarle nuovamente senza l'esplicita autorizzazione dell'utente per le richieste successive. Questo è un tentativo di fornire all'utente il modello "connesso / disconnesso" che si aspettano, ma è puramente un kludge. È il browser che simula questa persistenza di stato. Il web server non ne è completamente consapevole.

Quindi "disconnettersi", nel contesto di http-auth, è puramente una simulazione fornita dal browser, e quindi al di fuori dell'autorità del server.

Sì, ci sono kludges. Ma infrangono la RESTfulness (se questo è di valore per te) e non sono affidabili.

Se hai assolutamente bisogno di un modello connesso / disconnesso per l'autenticazione del tuo sito, la scommessa migliore è un cookie di tracciamento, con la persistenza dello stato memorizzata sul server in qualche modo (mysql, sqlite, flatfile, ecc.). Ciò richiederà che tutte le richieste siano valutate, ad esempio, con PHP.


26

Soluzione

Puoi farlo usando 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>

Ciò che viene fatto sopra è:

  • per IE : basta cancellare la cache di autenticazione e reindirizzare da qualche parte

  • per altri browser : inviare una richiesta XMLHttp dietro le quinte con nome e password di accesso "logout". Dobbiamo inviarlo a un percorso che restituirà 200 OK a quella richiesta (cioè non dovrebbe richiedere l'autenticazione HTTP).

Sostituisci '/where/to/redirect'con un percorso verso cui reindirizzare dopo la disconnessione e sostituisci '/path/that/will/return/200/OK'con un percorso sul tuo sito che restituirà 200 OK.


5
È in qualche modo una soluzione alternativa accedere come un altro utente. Ma questo in realtà funziona e merita più credito.
Charlie Rudenstål,

2
Penso che questa sia la risposta migliore. Come indicato in questa risposta a una domanda simile, potrebbe esserci qualche vantaggio nel randomizzare la password.
zelanix,

2
Questo era quello che volevo: funzionava in tutti i browser senza problemi. Ho mantenuto intatta la pagina di "logout" che ho ereditato. Non volevo necessariamente usare JS (forse irrazionalmente), ma le altre risposte avevano tutti problemi tra browser e questo ha funzionato perfettamente.
dgig

Non riesco a far funzionare questo nel modo in cui è spiegato. Quando sono tornato nell'area protetta, il browser si autentica nuovamente con l'invio delle ultime credenziali valide utilizzate nell'intestazione. Tuttavia, con un piccolo cambiamento ha funzionato per me. Ho modificato la risposta 200 OK con un'intestazione con lo stesso regno dell'area protetta, ma accettando solo un utente / passaggio "logout: logout". In questo modo, l'utente ha effettuato l'accesso con questo utente "logout" e questo è l'utente che riprova quando torna nell'area protetta. L'area protetta rifiuta questo utente / passaggio, in modo che l'utente possa modificare le proprie credenziali.
jonaguera,

2
Questo non funziona come è spiegato. Testato su Chrome 40 e Firefox 35.
funforums,

13

Soluzione alternativa (non una soluzione pulita, piacevole (o persino funzionante! Vedere i commenti)):

Disabilita le sue credenziali una volta.

Puoi spostare la tua logica di autenticazione HTTP su PHP inviando le intestazioni appropriate (se non hai effettuato l'accesso):

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

E analizzando l'input con:

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

Quindi disabilitare una volta le sue credenziali dovrebbe essere banale.


18
Il problema con questa soluzione è che: fai sapere a IE che le credenziali non sono ok. Visualizza la finestra di accesso con campi vuoti (non mostra i valori memorizzati nel gestore password). Ma quando si fa clic su Annulla e si aggiorna la pagina, vengono inviate le credenziali archiviate, quindi riconnettendosi.
Josef Sábl,

downvoted; Come ha commentato Josef Sable, questo non risolve il problema.
Chris Wesseling il

7

Disconnettersi da HTTP Basic Auth in due passaggi

Supponiamo che io abbia un dominio di autenticazione HTTP di base denominato "Password protetta" e che Bob abbia effettuato l'accesso. Per disconnettersi, faccio 2 richieste AJAX:

  1. Accedi allo script / logout_step1. Aggiunge un utente temporaneo casuale a .htusers e risponde con il suo login e password.
  2. Accedi allo script / logout_step2 autenticato con il login e la password dell'utente temporaneo . Lo script elimina l'utente temporaneo e aggiunge questa intestazione alla risposta:WWW-Authenticate: Basic realm="Password protected"

A questo punto il browser ha dimenticato le credenziali di Bob.


1
Wow! Questo merita davvero un +1 per pura inventiva, anche se è una cosa completamente pazza da fare.
Andy Triggs,

7

La mia soluzione al problema è la seguente. È possibile trovare la funzione http_digest_parse, $realme $usersnel secondo esempio di questa pagina: 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

In genere, una volta che un browser ha chiesto all'utente le credenziali e le ha fornite a un determinato sito Web, continuerà a farlo senza ulteriori richieste. A differenza dei vari modi in cui è possibile cancellare i cookie sul lato client, non conosco un modo simile per chiedere al browser di dimenticare le credenziali di autenticazione fornite.


Credo che ci sia un'opzione per eliminare le sessioni autenticate quando si seleziona "Elimina dati privati" in Firefox
Kristian J.

1
Inoltre l'estensione Web Developer Toolbar per Firefox offre funzionalità per eliminare le autenticazioni HTTP. Ma questo è fuori discussione in quanto non possiamo davvero chiedere ai nostri utenti di scaricare estensioni FF o eseguire comandi criptici del browser :-)
Josef Sábl

2
Il modo predefinito di Firefox per disconnettersi dall'autenticazione HTTP è disponibile in "Strumenti"> "Cancella cronologia recente ...", come casella di controllo "Login attivi". Questo non è né intuitivo né ti consente di disconnetterti da un solo dominio, ti disconnetti sempre da ogni pagina.
aef,

2

Trac - per impostazione predefinita - utilizza anche l'autenticazione HTTP. Il logout non funziona e non può essere corretto:

  • Questo è un problema con lo stesso schema di autenticazione HTTP e non c'è nulla che possiamo fare in Trac per risolverlo correttamente.
  • Al momento non esiste una soluzione alternativa (JavaScript o altro) che funzioni con tutti i principali browser.

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

Sembra che non ci sia una risposta attiva alla domanda, questo problema è stato segnalato sette anni fa e ha perfettamente senso: HTTP è apolide. O una richiesta viene effettuata con credenziali di autenticazione oppure no. Ma dipende dal client che invia la richiesta, non dal server che la riceve. Il server può solo dire se un URI di richiesta necessita di autorizzazione o meno.


2

Ho dovuto ripristinare l'autorizzazione .htaccess quindi ho usato questo:

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

Lo abbiamo trovato qui: http://php.net/manual/en/features.http-auth.php

Vai a capire.

Un certo numero di soluzioni risiede su quella pagina e si nota anche in fondo: Lynx, non cancella l'autent come altri browser;)

L'ho testato sui miei browser installati e una volta chiuso, ogni browser sembra che necessiti costantemente di riaccensione al rientro.


Questo non sembra funzionare, sto ricevendo il testo di annullamento senza alcuna casella di accesso pop-up.
Michael,

Si scopre che l'invio del WWW-Authenticateproblema stava causando, liberandomi automaticamente che mi disconnettevo.
Michael,

E viceversa, sembra che NON inviare WWW-Authenticatementre risolve il problema in un browser (Chrome) induca un altro browser (Firefox) a ricordare le credenziali e a inviarle alla richiesta successiva, con conseguente nuovo accesso automatico! Argh!
Michael,

Quindi guarda UA e fare l'uno o l'altro sembra una soluzione
Lennart Rolland,

2

Questa potrebbe non essere la soluzione cercata ma l'ho risolta in questo modo. ho 2 script per il processo di disconnessione.

logout.php

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

log.php

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

In questo modo non ricevo un avviso e la mia sessione è terminata


1
Questa è stata l'unica soluzione davvero funzionata per me! Testato su Firefox 37 e Chromium 41
Zesaver,

1

AFAIK, non esiste un modo pulito per implementare una funzione di "logout" quando si utilizza l'autenticazione htaccess (ovvero basata su HTTP).

Questo perché tale autenticazione utilizza il codice di errore HTTP '401' per indicare al browser che sono richieste le credenziali, a quel punto il browser richiede all'utente i dettagli. Da quel momento in poi, fino alla chiusura del browser, invierà sempre le credenziali senza ulteriori richieste.


1

La migliore soluzione che ho trovato finora è (è una specie di pseudo-codice, $isLoggedInè la pseudo variabile per http auth):

Al momento del "logout" è sufficiente memorizzare alcune informazioni nella sessione dicendo che l'utente è effettivamente disconnesso.

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

Nel punto in cui controllo l'autenticazione espanderò la condizione:

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

La sessione è in qualche modo collegata allo stato dell'autenticazione http, quindi l'utente rimane disconnesso fintanto che mantiene aperto il browser e fintanto che l'autenticazione http persiste nel browser.


4
Mentre l'autenticazione di base http è RESTful, le sessioni no.
Deamon,

1

Forse mi manca il punto.

Il modo più affidabile che ho trovato per terminare l'autenticazione HTTP è chiudere il browser e tutte le finestre del browser. Puoi chiudere una finestra del browser usando Javascript ma non credo che puoi chiudere tutte le finestre del browser.


a proposito alcuni browser non chiuderanno una finestra se è l'unica scheda aperta, quindi il punto è davvero discutibile
scape

Quei lunghi anni fa avevo il compito di implementare il pulsante di logout senza chiudere la finestra :-) Ma forse non si sarebbero soffermati sul "non chiudere la finestra". Ma hey, questa è una soluzione semplice che potrebbe funzionare per qualcuno e l'ho perso allora per essere onesti.
Josef Sábl,

1

L'unico modo efficace che ho trovato per cancellare le credenziali PHP_AUTH_DIGESTo PHP_AUTH_USERAND PHP_AUTH_PWè chiamare l'intestazione HTTP/1.1 401 Unauthorized.

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

0

Mentre gli altri hanno ragione nel dire che è impossibile disconnettersi dall'autenticazione HTTP di base, ci sono modi per implementare l'autenticazione che si comportano in modo simile. Un approccio ovvio è utilizzare auth_memcookie . Se vuoi davvero implementare l'autenticazione HTTP di base (cioè usare le finestre di dialogo del browser per accedere piuttosto che un modulo HTTP) usando questo - basta impostare l'autenticazione su una directory protetta .htaccess separata contenente uno script PHP che reindirizza indietro dove è arrivato l'utente creazione della sessione memcache.


0

Ci sono molte risposte grandi - complesse - qui. Nel mio caso particolare ho trovato una soluzione semplice e pulita per il logout. Devo ancora testare su Edge. Nella mia pagina a cui ho effettuato l'accesso, ho inserito un collegamento di disconnessione simile a questo:

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

E a capo di quella pagina logout.html (che è anche protetta da .htaccess) ho un aggiornamento della pagina simile a questo:

<meta http-equiv="Refresh" content="0; url=https://logout:logout@MyDomainHere.net/" />

Dove lasciare le parole "logout" sul posto per cancellare il nome utente e la password memorizzati nella cache per il sito.

Devo ammettere che se fosse necessario poter accedere direttamente da più pagine dall'inizio, ognuno di quei punti di ingresso avrebbe bisogno della propria pagina logout.html corrispondente. In caso contrario, è possibile centralizzare il logout introducendo un passaggio gatekeeper aggiuntivo nel processo prima del prompt di accesso effettivo, che richiede l'inserimento di una frase per raggiungere una destinazione di accesso.


1
quando si procede in avanti, funziona, si disconnette, ma la cronologia del browser può ancora ristabilire la sessione.
johnwayne,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.