Logout otentikasi HTTP melalui PHP

151

Apa cara yang benar untuk keluar dari folder yang dilindungi otentikasi HTTP?

Ada beberapa solusi yang dapat mencapai ini, tetapi mereka berpotensi berbahaya karena dapat buggy atau tidak berfungsi dalam situasi / browser tertentu. Itu sebabnya saya mencari solusi yang benar dan bersih.

Josef Sábl
sumber
Silakan tentukan tujuan untuk keluar Anda. Haruskah ini logout paksa (user-deactivation)? Fungsi logout sederhana untuk pengguna? Ada yang lain?
Karsten
6
Saya tidak mengerti mengapa ini penting, tetapi keduanya adalah kasus: penonaktifan berdasarkan kondisi internal dalam aplikasi serta tombol logout yang khas. Tolong jelaskan mengapa ini penting, saya akan mengeditnya langsung ke pertanyaan.
Josef Sábl
2
"Solusi yang benar dan bersih" adalah browser yang memiliki tombol logout sendiri yang, ketika diklik, akan membuat browser berhenti mengirim tajuk Auth ... Yang bisa diimpikan, kan?
DanMan
1
Bilah Alat Pengembang Web memiliki "tombol" tersebut.
Josef Sábl
Apa yang dikatakan Josef: toolbar pengembang web untuk Firefox ->Miscellaneous -> Clear Private Data -> HTTP Authentication
Yarin

Jawaban:

103

Mu Tidak ada cara yang benar , tidak satu pun yang konsisten di seluruh browser.

Ini adalah masalah yang berasal dari spesifikasi HTTP (bagian 15.6):

Klien HTTP dan agen pengguna yang ada biasanya menyimpan informasi otentikasi tanpa batas. HTTP / 1.1. tidak menyediakan metode bagi server untuk mengarahkan klien untuk membuang kredensial yang di-cache ini.

Di sisi lain, bagian 10.4.2 mengatakan:

Jika permintaan sudah menyertakan kredensial Otorisasi, maka respons 401 menunjukkan bahwa otorisasi telah ditolak untuk kredensial tersebut. Jika respons 401 berisi tantangan yang sama dengan respons sebelumnya, dan agen pengguna telah mencoba otentikasi setidaknya satu kali, maka pengguna HARUS disajikan entitas yang diberikan dalam respons, karena entitas itu mungkin menyertakan informasi diagnostik yang relevan.

Dengan kata lain, Anda mungkin dapat menampilkan kotak login lagi (seperti kata @Karsten ), tetapi browser tidak harus memenuhi permintaan Anda - jadi jangan terlalu bergantung pada fitur (mis) ini.

Piskvor meninggalkan gedung
sumber
9
Ini adalah bug di RFC. W3C terlalu malas untuk diperbaiki. Sangat sedih.
Erik Aronesty
Seperti yang disarankan @Jonathan Hanson di bawah ini , Anda dapat menggunakan cookie pelacakan bersama dengan otentikasi HTTP. Ini adalah metode terbaik untuk saya.
machineaddict
61

Metode yang bekerja dengan baik di Safari. Juga berfungsi di Firefox dan Opera, tetapi dengan peringatan.

Location: http://[email protected]/

Ini memberi tahu browser untuk membuka URL dengan nama pengguna baru, menggantikan yang sebelumnya.

Kornel
sumber
14
Menurut RFC 3986 (URI: Generic Syntax) bagian 3.2.1. (Informasi Pengguna) penggunaan user:password@hostsudah tidak digunakan lagi. Hanya menggunakan http://[email protected]/tidak dan harus bekerja dalam banyak kasus.
aef
1
@ andho: ya, ini redirect. Anda harus menggunakannya dengan status 302.
Kornel
1
Rupanya tautan sederhana ke [email protected] juga berfungsi (tautan "putuskan" ke URL ini) alih-alih pengalihan http di PHP ... ada kerugiannya?
moala
4
Hati-hati: Pengajuan formulir menggunakan jalur relatif mungkin gagal ketika dilakukan setelah login ulang (masuk dengan prompt logout), karena alamat tersebut masih berupa [email protected]/path dan bukan yourserver.example.com/path /
Jason
1
[email protected] menangani masalah whitout di Chrome, tetapi meminta pertanyaan keamanan di Firefox. logout: [email protected] dosis tidak membuat Firefox menjanjikan pertanyaan keamanan. Tidak satu pun dari kedua url berfungsi di IE8: /
Thor A. Pedersen
46

Jawaban sederhana adalah bahwa Anda tidak dapat secara andal keluar dari autentikasi http.

Jawaban panjangnya:
Http-auth (seperti spesifikasi HTTP lainnya) dimaksudkan untuk menjadi stateless. Jadi "masuk" atau "keluar" sebenarnya bukan konsep yang masuk akal. Cara yang lebih baik untuk melihatnya adalah dengan bertanya, untuk setiap permintaan HTTP (dan ingat pemuatan halaman biasanya banyak permintaan), "apakah Anda diizinkan melakukan apa yang Anda minta?". Server melihat setiap permintaan sebagai baru dan tidak terkait dengan permintaan sebelumnya.

Peramban telah memilih untuk mengingat kredensial yang Anda beri tahu pada 401 pertama, dan mengirimnya kembali tanpa izin eksplisit pengguna atas permintaan berikutnya. Ini merupakan upaya untuk memberi pengguna model "masuk / keluar" yang mereka harapkan, tetapi ini murni kludge. Ini adalah browser yang mensimulasikan ketekunan negara ini. Server web sama sekali tidak menyadarinya.

Jadi "logout", dalam konteks http-auth adalah murni simulasi yang disediakan oleh browser, dan di luar otoritas server.

Ya, ada kludges. Tetapi mereka merusak ISTIRAHAT (jika itu bernilai bagi Anda) dan mereka tidak dapat diandalkan.

Jika Anda benar-benar memerlukan model masuk / keluar untuk otentikasi situs Anda, taruhan terbaik adalah cookie pelacakan, dengan persistensi status yang disimpan di server dalam beberapa cara (mysql, sqlite, flatfile, dll). Ini akan mengharuskan semua permintaan dievaluasi, misalnya, dengan PHP.

Jonathan Hanson
sumber
26

Penanganan masalah

Anda dapat melakukan ini menggunakan 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>

Apa yang dilakukan di atas adalah:

  • untuk IE - cukup bersihkan cache auth dan arahkan ke suatu tempat

  • untuk browser lain - kirim XMLHttpRequest di belakang layar dengan nama login dan kata sandi 'logout'. Kami perlu mengirimkannya ke beberapa jalur yang akan mengembalikan 200 OK untuk permintaan itu (artinya tidak memerlukan otentikasi HTTP).

Ganti '/where/to/redirect'dengan beberapa jalur untuk dialihkan ke setelah logout dan ganti '/path/that/will/return/200/OK'dengan beberapa jalur di situs Anda yang akan mengembalikan 200 OK.

Anton Mochalin
sumber
5
Ini semacam solusi untuk masuk sebagai pengguna lain. Tetapi ini benar-benar berfungsi dan layak mendapatkan lebih banyak kredit.
Charlie Rudenstål
2
Saya pikir ini adalah jawaban terbaik. Seperti yang dinyatakan dalam jawaban ini untuk pertanyaan serupa mungkin ada beberapa keuntungan untuk mengacak kata sandi.
zelanix
2
Inilah yang saya inginkan - bekerja di semua browser tanpa masalah. Terus halaman "logout" yang saya warisi secara utuh. Saya tidak selalu ingin menggunakan JS (mungkin tidak rasional), tetapi jawaban lain semua memiliki masalah lintas-browser dan ini bekerja dengan sempurna.
dgig
Saya tidak bisa membuat pekerjaan ini dengan cara dijelaskan. Ketika saya kembali ke area aman, browser mengotentikasi dirinya kembali dengan mengirimkan kredensial valid yang digunakan terakhir di header. Namun, dengan sedikit perubahan itu berhasil bagi saya. Saya mengubah respons 200 OK dengan header dengan Realm yang sama dengan area yang diamankan, tetapi hanya menerima pengguna / pass "logout: logout". Dengan cara ini, pengguna masuk dengan pengguna "logout" ini, dan ini adalah pengguna yang mencoba lagi ketika dia kembali ke area aman. Area aman menolak pengguna / pass ini, sehingga pengguna dapat mengubah kredensial.
jonaguera
2
Ini tidak berfungsi seperti yang dijelaskan. Diuji di Chrome 40 dan Firefox 35.
funforums
13

Solusi (bukan solusi yang bersih, baik (atau bahkan berfungsi! Lihat komentar)):

Nonaktifkan kredensial satu kali.

Anda dapat memindahkan logika otentikasi HTTP ke PHP dengan mengirimkan tajuk yang sesuai (jika tidak masuk):

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

Dan parsing input dengan:

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

Jadi menonaktifkan kredensial satu kali harus sepele.

Karsten
sumber
18
Masalah dengan solusi ini adalah: Anda membiarkan IE tahu bahwa kredensial tidak OK. Ini menampilkan dialog login dengan bidang kosong (tidak menunjukkan nilai yang disimpan dalam pengelola kata sandi). Tetapi ketika Anda mengklik membatalkan dan menyegarkan halaman, itu mengirimkan kredensial yang disimpan, sehingga masuk lagi.
Josef Sábl
Diturunkan; Seperti komentar Josef Sable, ini tidak menyelesaikan masalah yang ada.
Chris Wesseling
7

Logout dari HTTP Basic Auth dalam dua langkah

Katakanlah saya memiliki ranah Autentikasi HTTP Dasar yang bernama "Proteksi kata sandi", dan Bob masuk. Untuk keluar saya membuat 2 permintaan AJAX:

  1. Akses skrip / logout_step1. Ia menambahkan pengguna sementara acak ke .htusers dan merespons dengan login dan kata sandinya.
  2. Akses skrip / logout_step2 diautentikasi dengan login dan kata sandi pengguna sementara . Script menghapus pengguna sementara dan menambahkan header ini pada respons:WWW-Authenticate: Basic realm="Password protected"

Pada titik ini browser lupa kredensial Bob.

Vlad GURDIGA
sumber
1
Wow! Ini benar-benar layak diberi +1 untuk daya cipta semata, bahkan jika itu sepenuhnya gila.
Andy Triggs
7

Solusi saya untuk masalah ini adalah sebagai berikut. Anda dapat menemukan fungsinya http_digest_parse, $realmdan $usersdalam contoh kedua halaman ini: 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
sumber
4

Biasanya, setelah browser meminta kredensial pengguna dan memasok mereka ke situs web tertentu, ia akan terus melakukannya tanpa diminta lebih lanjut. Berbeda dengan berbagai cara Anda dapat menghapus cookie di sisi klien, saya tidak tahu cara serupa untuk meminta browser melupakan kredensial otentikasi yang disediakan.

Greg Hewgill
sumber
Saya percaya ada opsi untuk menghapus sesi yang diautentikasi ketika Anda memilih "Hapus data pribadi" di Firefox
Kristian J.
1
Ekstensi Toolbar Pengembang Web untuk Firefox juga menawarkan fitur untuk menghapus Otentikasi HTTP. Tapi ini tidak mungkin karena kami benar-benar tidak dapat meminta pengguna kami untuk mengunduh ekstensi FF atau menjalankan perintah peramban samar :-)
Josef Sábl
2
Cara default Firefox untuk keluar dari HTTP auth tersedia di "Tools"> "Clear History Terbaru ...", sebagai kotak centang "Active Login". Ini tidak intuitif dan tidak memungkinkan Anda untuk hanya logout dari satu domain, Anda selalu logout dari setiap halaman.
aef
2

Trac - secara default - juga menggunakan Otentikasi HTTP. Logout tidak berfungsi dan tidak bisa diperbaiki:

  • Ini adalah masalah dengan skema otentikasi HTTP itu sendiri, dan tidak ada yang bisa kita lakukan di Trac untuk memperbaikinya dengan benar.
  • Saat ini tidak ada solusi (JavaScript atau lainnya) yang berfungsi dengan semua browser utama.

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

Sepertinya tidak ada jawaban yang berfungsi untuk pertanyaan itu, masalah itu telah dilaporkan tujuh tahun lalu dan masuk akal: HTTP itu tidak bernegara. Salah satu permintaan dilakukan dengan kredensial otentikasi atau tidak. Tapi itu masalah klien yang mengirim permintaan, bukan server yang menerimanya. Server hanya dapat mengatakan apakah permintaan URI memerlukan otorisasi atau tidak.

hakre
sumber
2

Saya perlu mengatur ulang otorisasi .htaccess jadi saya menggunakan ini:

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

Ditemukan di sini: http://php.net/manual/en/features.http-auth.php

Sosok pergi.

Sejumlah solusi berada di halaman itu dan bahkan dicatat di bagian bawah: Lynx, tidak menghapus auth seperti browser lainnya;)

Saya mengujinya di browser yang diinstal dan sekali ditutup, setiap browser sepertinya secara konsisten memerlukan reauth pada masuk kembali.

Dooley
sumber
Ini sepertinya tidak berfungsi, saya mendapatkan teks pembatalan tanpa kotak masuk sembulan.
Michael
Ternyata, mengirim WWW-Authenticateitu menyebabkan masalah, menyingkirkan itu log out secara otomatis.
Michael
Dan sebaliknya, tampaknya BUKAN mengirimkan WWW-Authenticatesementara memperbaiki masalah di satu browser (Chrome) menyebabkan browser lain (Firefox) mengingat kredensial dan mengirimkannya pada permintaan berikutnya, menghasilkan login ulang otomatis! Argh!
Michael
Kemudian lihat UA dan lakukan satu atau yang lain sepertinya solusi
Lennart Rolland
2

Ini mungkin bukan solusi yang dicari tetapi saya menyelesaikannya seperti ini. saya punya 2 skrip untuk proses logout.

logout.php

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

log.php

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

Dengan cara ini saya tidak mendapat peringatan dan sesi saya dihentikan

Kevin
sumber
1
Ini adalah satu-satunya solusi yang benar-benar bekerja untuk saya! Diuji pada Firefox 37 dan Chromium 41
zesaver
1

AFAIK, tidak ada cara bersih untuk mengimplementasikan fungsi "logout" saat menggunakan otentikasi htaccess (yaitu berbasis HTTP).

Ini karena otentikasi tersebut menggunakan kode kesalahan HTTP '401' untuk memberi tahu browser bahwa kredensial diperlukan, pada saat mana browser meminta detail dari pengguna. Sejak saat itu, hingga browser ditutup, itu akan selalu mengirim kredensial tanpa diminta lebih lanjut.

Alnitak
sumber
1

Solusi terbaik yang saya temukan sejauh ini (itu adalah semacam pseudo-code, $isLoggedInvariabel pseudo untuk http auth):

Pada saat "logout" hanya menyimpan beberapa info ke sesi yang mengatakan bahwa pengguna benar-benar keluar.

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

Di tempat saya memeriksa otentikasi, saya memperluas kondisi:

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

Sesi agak ditautkan dengan status otentikasi http sehingga pengguna tetap logout selama dia tetap membuka browser dan selama otentikasi http tetap ada di browser.

Josef Sábl
sumber
4
Meskipun otentikasi dasar http TENANG, sesi tidak.
deamon
1

Mungkin saya kehilangan intinya.

Cara paling andal yang saya temukan untuk mengakhiri otentikasi HTTP adalah dengan menutup browser dan semua jendela browser. Anda dapat menutup jendela browser menggunakan Javascript tapi saya rasa Anda tidak bisa menutup semua jendela browser.

Toby Allen
sumber
fyi beberapa browser tidak akan menutup jendela jika itu adalah satu-satunya tab yang terbuka, jadi intinya benar
scape
Beberapa tahun yang lalu saya punya tugas untuk mengimplementasikan tombol logout tanpa menutup jendela :-) Tapi mungkin mereka tidak akan memikirkan "tidak menutup jendela". Tapi hei, ini adalah solusi sederhana yang mungkin bekerja untuk seseorang dan saya melewatkannya saat itu jujur.
Josef Sábl
1

Satu-satunya cara efektif saya telah menemukan untuk menghapus PHP_AUTH_DIGESTatau PHP_AUTH_USERDAN PHP_AUTH_PWkredensial adalah untuk memanggil header HTTP/1.1 401 Unauthorized.

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

Sementara yang lain benar dalam mengatakan bahwa tidak mungkin untuk logout dari otentikasi http dasar ada cara untuk menerapkan otentikasi yang berperilaku sama. Salah satu pendekatan yang jelas adalah menggunakan auth_memcookie . Jika Anda benar-benar ingin menerapkan otentikasi HTTP Dasar (mis. Gunakan dialog browser untuk masuk lebih dalam daripada bentuk HTTP) menggunakan ini - cukup setel otentikasi ke direktori terproteksi .htaccess terpisah yang berisi skrip PHP yang mengarahkan kembali ke tempat pengguna datang setelah membuat sesi memcache.

symcbean
sumber
0

Ada banyak jawaban hebat - kompleks - di sini. Dalam kasus khusus saya, saya menemukan perbaikan yang bersih dan sederhana untuk logout. Saya belum menguji di Edge. Di halaman saya yang telah saya masuki, saya telah menempatkan tautan logout yang mirip dengan ini:

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

Dan di bagian atas halaman logout.html (yang juga dilindungi oleh .htaccess) saya memiliki refresh halaman yang mirip dengan ini:

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

Di mana Anda akan meninggalkan kata "logout" untuk menghapus nama pengguna dan kata sandi yang di-cache untuk situs tersebut.

Saya akui bahwa jika beberapa halaman harus dapat login langsung dari awal, masing-masing titik masuk akan membutuhkan halaman logout.html masing-masing yang sesuai. Kalau tidak, Anda bisa memusatkan logout dengan memperkenalkan langkah penjaga gerbang tambahan ke dalam proses sebelum prompt login yang sebenarnya, membutuhkan entri frasa untuk mencapai tujuan login.

Johnwayne
sumber
1
ketika bergerak maju, ini berfungsi, itu memang keluar, tetapi riwayat kembali browser masih dapat membangun kembali sesi.
johnwayne