Déconnexion de l'authentification HTTP via PHP

151

Quelle est la bonne façon de se déconnecter du dossier protégé par authentification HTTP?

Il existe des solutions de contournement qui peuvent y parvenir, mais elles sont potentiellement dangereuses car elles peuvent être boguées ou ne pas fonctionner dans certaines situations / navigateurs. C'est pourquoi je recherche une solution correcte et propre.

Josef Sábl
la source
Veuillez préciser le but de votre déconnexion. Doit-il s'agir d'une déconnexion forcée (désactivation par l'utilisateur)? Fonction de déconnexion simple pour l'utilisateur? Rien d'autre?
Karsten
6
Je ne comprends pas pourquoi c'est important, mais c'est les deux cas: la désactivation basée sur les conditions internes de l'application ainsi que le bouton de déconnexion typique. Veuillez expliquer pourquoi c'est important, je le modifierai directement dans la question.
Josef Sábl
2
La "solution correcte et propre" serait que les navigateurs aient leur propre bouton de déconnexion qui, lorsqu'on clique dessus, fera arrêter le navigateur d'envoyer les en-têtes Auth ... On peut rêver, non?
DanMan
1
La barre d'outils Web Developer a un tel "bouton".
Josef Sábl
Ce que Josef a dit: barre d'outils de développement Web pour Firefox ->Miscellaneous -> Clear Private Data -> HTTP Authentication
Yarin

Réponses:

103

Mu. Il n'existe aucune méthode correcte , même pas cohérente entre les navigateurs.

C'est un problème qui vient de la spécification HTTP (section 15.6):

Les clients HTTP et les agents utilisateurs existants conservent généralement les informations d'authentification indéfiniment. HTTP / 1.1. ne fournit pas de méthode permettant à un serveur de demander aux clients de supprimer ces informations d'identification mises en cache.

D'autre part, la section 10.4.2 dit:

Si la demande comprenait déjà des informations d'identification d'autorisation, la réponse 401 indique que l'autorisation a été refusée pour ces informations d'identification. Si la réponse 401 contient le même défi que la réponse précédente, et que l'agent utilisateur a déjà tenté l'authentification au moins une fois, alors l'utilisateur DEVRAIT se voir présenter l'entité qui a été donnée dans la réponse, puisque cette entité pourrait inclure des informations de diagnostic pertinentes.

En d'autres termes, vous pourrez peut-être afficher à nouveau la boîte de connexion (comme le dit @Karsten ), mais le navigateur n'a pas à honorer votre demande - alors ne dépendez pas trop de cette (mauvaise) fonctionnalité.

Piskvor a quitté le bâtiment
la source
9
Il s'agit d'un bogue dans la RFC. W3C trop paresseux pour réparer. Si triste.
Erik Aronesty
Comme @Jonathan Hanson l'a suggéré ci - dessous , vous pouvez utiliser un cookie de suivi avec l'authentification HTTP. C'est la meilleure méthode pour moi.
machineaddict le
61

Méthode qui fonctionne bien dans Safari. Fonctionne également dans Firefox et Opera, mais avec un avertissement.

Location: http://[email protected]/

Cela indique au navigateur d'ouvrir l'URL avec un nouveau nom d'utilisateur, en remplaçant le précédent.

Kornel
la source
14
Selon RFC 3986 (URI: Generic Syntax) section 3.2.1. (Informations utilisateur) l'utilisation de user:password@hostest obsolète. Utiliser uniquement http://[email protected]/n'est pas et devrait fonctionner dans la plupart des cas.
aef
1
@andho: oui, c'est une redirection. Vous devriez l'utiliser avec le statut 302.
Kornel
1
Apparemment, un simple lien vers [email protected] fonctionne également (un lien de "déconnexion" vers cette URL) au lieu d'une redirection http en PHP ... un inconvénient à cela?
moala
4
Attention: la soumission de formulaire à l'aide d'un chemin relatif peut échouer lorsqu'elle est effectuée après une reconnexion (connectez-vous avec l'invite de déconnexion), car l'adresse serait toujours [email protected]/path et non yourserver.example.com/path /
Jason
1
[email protected] fonctionne sans problème dans Chrome, mais demande une question de sécurité dans Firefox. logout: [email protected] ne fait pas de Firefox promt une question de sécurité. Aucune des deux URL ne fonctionne dans IE8: /
Thor A. Pedersen
46

La réponse simple est que vous ne pouvez pas vous déconnecter de manière fiable de l'authentification http.

La réponse longue:
Http-auth (comme le reste de la spécification HTTP) est censé être sans état. Donc être «connecté» ou «déconnecté» n'est pas vraiment un concept qui a du sens. La meilleure façon de le voir est de demander, pour chaque requête HTTP (et rappelez-vous qu'une page est généralement chargée en plusieurs requêtes), "êtes-vous autorisé à faire ce que vous demandez?". Le serveur considère chaque demande comme nouvelle et sans rapport avec les demandes précédentes.

Les navigateurs ont choisi de se souvenir des informations d'identification que vous leur avez fournies sur le premier 401 et de les renvoyer sans l'autorisation explicite de l'utilisateur sur les demandes suivantes. Il s'agit d'une tentative de donner à l'utilisateur le modèle «connecté / déconnecté» auquel il s'attend, mais c'est purement un kludge. C'est le navigateur qui simule cette persistance d'état. Le serveur Web n'en est absolument pas conscient.

Donc "se déconnecter", dans le contexte de http-auth est purement une simulation fournie par le navigateur, et donc en dehors de l'autorité du serveur.

Oui, il y a des kludges. Mais ils cassent RESTful-ness (si cela vous intéresse) et ils ne sont pas fiables.

Si vous avez absolument besoin d'un modèle connecté / déconnecté pour l'authentification de votre site, le meilleur pari est un cookie de suivi, avec la persistance de l'état stocké sur le serveur d'une manière ou d'une autre (mysql, sqlite, flatfile, etc.). Cela nécessitera que toutes les demandes soient évaluées, par exemple, avec PHP.

Jonathan Hanson
la source
26

solution de contournement

Vous pouvez le faire en utilisant 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>

Ce qui est fait ci-dessus est:

  • pour IE - effacez simplement le cache d'authentification et redirigez quelque part

  • pour les autres navigateurs - envoyez une requête XMLHttpRequest dans les coulisses avec le nom de connexion et le mot de passe «déconnexion». Nous devons l'envoyer à un chemin qui renverra 200 OK à cette demande (c'est-à-dire qu'il ne devrait pas nécessiter d'authentification HTTP).

Remplacez '/where/to/redirect'par un chemin vers '/path/that/will/return/200/OK'lequel rediriger après la déconnexion et remplacez-le par un chemin sur votre site qui renverra 200 OK.

Anton Mochalin
la source
5
C'est en quelque sorte une solution de contournement pour se connecter en tant qu'autre utilisateur. Mais cela fonctionne réellement et mérite plus de crédit.
Charlie Rudenstål
2
Je pense que c'est la meilleure réponse. Comme indiqué dans cette réponse à une question similaire, il peut y avoir un avantage à randomiser le mot de passe.
zelanix
2
C'était ce que je voulais - fonctionnait dans tous les navigateurs sans problème. J'ai conservé intact la page de "déconnexion" dont j'ai hérité. Je ne voulais pas nécessairement utiliser JS (peut-être de manière irrationnelle), mais les autres réponses avaient toutes des problèmes entre navigateurs et cela fonctionnait parfaitement.
dgig
Je ne peux pas faire fonctionner cela de la manière expliquée. Lorsque je suis retourné dans la zone sécurisée, le navigateur s'authentifie à nouveau en envoyant les dernières informations d'identification valides utilisées dans l'en-tête. Cependant, avec un petit changement, cela a fonctionné pour moi. J'ai changé la réponse 200 OK avec un en-tête avec le même royaume de la zone sécurisée, mais en acceptant seulement un utilisateur / pass "déconnexion: déconnexion". De cette manière, l'utilisateur s'est connecté avec cet utilisateur "déconnexion", et c'est l'utilisateur qui réessaye lorsqu'il retourne dans la zone sécurisée. La zone sécurisée rejette cet utilisateur / pass, afin que l'utilisateur puisse modifier ses informations d'identification.
jonaguera
2
Cela ne fonctionne pas comme il est expliqué. Testé dans Chrome 40 et Firefox 35.
funforums
13

Solution de contournement (pas une solution propre, agréable (ou même fonctionnelle! Voir les commentaires)):

Désactivez ses informations d'identification une fois.

Vous pouvez déplacer votre logique d'authentification HTTP vers PHP en envoyant les en-têtes appropriés (si vous n'êtes pas connecté):

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

Et analyser l'entrée avec:

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

Donc, désactiver ses informations d'identification une fois devrait être trivial.

Karsten
la source
18
Le problème avec cette solution est que: Vous faites savoir à IE que les informations d'identification ne sont pas correctes. Il affiche une boîte de dialogue de connexion avec des champs vides (ne montrant pas les valeurs stockées dans le gestionnaire de mots de passe). Mais lorsque vous cliquez sur Annuler et actualisez la page, il envoie les informations d'identification stockées, se reconnectant ainsi.
Josef Sábl
Downvoté; Comme l'a commenté Josef Sable, cela ne résout pas le problème.
Chris Wesseling
7

Déconnexion de HTTP Basic Auth en deux étapes

Disons que j'ai un domaine HTTP Basic Auth nommé «Password protected» et que Bob est connecté. Pour me déconnecter, je fais 2 requêtes AJAX:

  1. Accédez au script / logout_step1. Il ajoute un utilisateur temporaire aléatoire à .htusers et répond avec son identifiant et son mot de passe.
  2. Accédez au script / logout_step2 authentifié avec le login et le mot de passe de l'utilisateur temporaire . Le script supprime l'utilisateur temporaire et ajoute cet en-tête à la réponse:WWW-Authenticate: Basic realm="Password protected"

À ce stade, le navigateur a oublié les informations d'identification de Bob.

Vlad GURDIGA
la source
1
Hou la la! Cela mérite vraiment un +1 pour sa pure inventivité, même si c'est une chose complètement folle à faire.
Andy Triggs
7

Ma solution au problème est la suivante. Vous pouvez trouver la fonction http_digest_parse, $realmet $usersdans le deuxième exemple de cette page: 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;
}
Tarte86
la source
4

En règle générale, une fois qu'un navigateur a demandé à l'utilisateur des informations d'identification et les a fournies à un site Web particulier, il continuera à le faire sans autre invite. Contrairement aux différentes façons dont vous pouvez effacer les cookies côté client, je ne connais pas de manière similaire de demander au navigateur d'oublier les informations d'authentification fournies.

Greg Hewgill
la source
Je crois qu'il existe une option pour supprimer les sessions authentifiées lorsque vous sélectionnez "Supprimer les données privées" dans Firefox
Kristian J.
1
L'extension Web Developer Toolbar pour Firefox offre également une fonctionnalité permettant de supprimer les authentifications HTTP. Mais c'est hors de question car nous ne pouvons vraiment pas demander à nos utilisateurs de télécharger des extensions FF ou d'exécuter des commandes de navigateur
cryptées
2
La méthode par défaut de Firefox pour se déconnecter de l'authentification HTTP est disponible sous "Outils"> "Effacer l'historique récent ...", comme case à cocher "Connexions actives". Ce n'est ni intuitif ni ne vous permet de vous déconnecter d'un seul domaine, vous vous déconnectez toujours de chaque page.
aef
2

Trac - par défaut - utilise également l'authentification HTTP. La déconnexion ne fonctionne pas et ne peut pas être corrigée:

  • C'est un problème avec le schéma d'authentification HTTP lui-même, et nous ne pouvons rien faire dans Trac pour le résoudre correctement.
  • Il n'existe actuellement aucune solution de contournement (JavaScript ou autre) qui fonctionne avec tous les principaux navigateurs.

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

On dirait qu'il n'y a pas de réponse fonctionnelle à la question, ce problème a été signalé il y a sept ans et c'est parfaitement logique: HTTP est sans état. Soit une demande est effectuée avec des informations d'authentification ou non. Mais c'est une question du client qui envoie la demande, pas du serveur qui la reçoit. Le serveur peut seulement dire si un URI de demande a besoin d'une autorisation ou non.

hakre
la source
2

J'avais besoin de réinitialiser l'autorisation .htaccess, j'ai donc utilisé ceci:

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

Trouvé ici: http://php.net/manual/en/features.http-auth.php

Allez comprendre.

Un certain nombre de solutions résident sur cette page et il note même en bas: Lynx, ne supprime pas l'authentification comme les autres navigateurs;)

Je l'ai testé sur mes navigateurs installés et une fois fermé, chaque navigateur semble nécessiter une réautorisation lors de la réentrée.

Dooley
la source
Cela ne semble pas fonctionner, je reçois le texte d'annulation sans aucune boîte de connexion contextuelle.
Michael
Il s'avère que l'envoi de la WWW-Authenticatecausait le problème, me débarrassant de ce qui m'a déconnecté automatiquement.
Michael
Et inversement, il semble que ne PAS envoyer le WWW-Authenticatetout en résolvant le problème dans un navigateur (Chrome) oblige un autre navigateur (Firefox) à mémoriser les informations d'identification et à les envoyer à la demande suivante, ce qui entraîne une reconnexion automatique! Argh!
Michael
Ensuite, regardez UA et faites l'un ou l'autre semble être une solution
Lennart Rolland
2

Ce n'est peut-être pas la solution recherchée mais je l'ai résolue comme ça. J'ai 2 scripts pour le processus de déconnexion.

logout.php

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

log.php

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

De cette façon, je ne reçois pas d'avertissement et ma session est terminée

Kevin
la source
1
C'était la seule solution qui fonctionnait vraiment pour moi! Testé sur Firefox 37 et Chromium 41
zesaver
1

AFAIK, il n'y a pas de moyen propre d'implémenter une fonction de "déconnexion" lors de l'utilisation de l'authentification htaccess (c'est-à-dire basée sur HTTP).

En effet, une telle authentification utilise le code d'erreur HTTP «401» pour indiquer au navigateur que des informations d'identification sont requises, auquel cas le navigateur invite l'utilisateur à fournir les détails. À partir de là, jusqu'à ce que le navigateur soit fermé, il enverra toujours les informations d'identification sans autre invite.

Alnitak
la source
1

La meilleure solution que j'ai trouvée jusqu'à présent est (c'est une sorte de pseudo-code, la $isLoggedInpseudo variable est pour http auth):

Au moment de la "déconnexion", enregistrez simplement des informations sur la session indiquant que l'utilisateur est réellement déconnecté.

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

À l'endroit où je vérifie l'authentification, j'étends la condition:

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

La session est quelque peu liée à l'état de l'authentification http, de sorte que l'utilisateur reste déconnecté tant qu'il garde le navigateur ouvert et tant que l'authentification http persiste dans le navigateur.

Josef Sábl
la source
4
Alors que l'authentification de base http est RESTful, les sessions ne le sont pas.
démon
1

Peut-être que je rate le point.

Le moyen le plus fiable que j'ai trouvé pour mettre fin à l'authentification HTTP est de fermer le navigateur et toutes les fenêtres du navigateur. Vous pouvez fermer une fenêtre de navigateur en utilisant Javascript mais je ne pense pas que vous puissiez fermer toutes les fenêtres de navigateur.

Toby Allen
la source
fyi certains navigateurs ne fermeront pas une fenêtre si c'est le seul onglet ouvert, donc le point est vraiment discutable
scape
Il y a longtemps, j'avais la tâche d'implémenter le bouton de déconnexion sans fermer la fenêtre :-) Mais peut-être qu'ils ne s'attarderaient pas sur "ne pas fermer la fenêtre". Mais bon, c'est une solution simple qui pourrait fonctionner pour quelqu'un et je l'ai ratée à l'époque pour être honnête.
Josef Sábl
1

Le seul moyen efficace que j'ai trouvé pour effacer les informations d'identification PHP_AUTH_DIGESTou PHP_AUTH_USERAND PHP_AUTH_PWest d'appeler l'en-tête HTTP/1.1 401 Unauthorized.

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

Alors que les autres ont raison de dire qu'il est impossible de se déconnecter de l'authentification http de base, il existe des moyens d'implémenter l'authentification qui se comportent de manière similaire. Une approche évidente consiste à utiliser auth_memcookie . Si vous voulez vraiment implémenter l'authentification HTTP de base (c'est-à-dire utiliser les boîtes de dialogue du navigateur pour vous connecter plutôt qu'un formulaire HTTP) en utilisant ceci - définissez simplement l'authentification sur un répertoire protégé .htaccess séparé contenant un script PHP qui redirige vers l'endroit où l'utilisateur est venu après création de la session Memcache.

symcbean
la source
0

Il y a beaucoup de réponses géniales - complexes - ici. Dans mon cas particulier, j'ai trouvé une solution simple et claire pour la déconnexion. Je n'ai pas encore testé dans Edge. Sur ma page à laquelle je me suis connecté, j'ai placé un lien de déconnexion similaire à celui-ci:

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

Et dans la tête de cette page logout.html (qui est également protégée par le .htaccess), j'ai un rafraîchissement de page similaire à celui-ci:

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

Où vous laisseriez les mots «déconnexion» en place pour effacer le nom d'utilisateur et le mot de passe mis en cache pour le site.

J'admets que si plusieurs pages devaient pouvoir être directement connectées depuis le début, chacun de ces points d'entrée aurait besoin de sa propre page logout.html correspondante. Sinon, vous pouvez centraliser la déconnexion en introduisant une étape de contrôleur d'accès supplémentaire dans le processus avant l'invite de connexion réelle, ce qui nécessite la saisie d'une phrase pour atteindre une destination de connexion.

Johnwayne
la source
1
lorsque vous avancez, cela fonctionne, il se déconnecte, mais l'historique du navigateur peut toujours rétablir la session.
johnwayne