Đăng xuất xác thực HTTP qua PHP


151

Là gì đúng cách để thoát ra của HTTP authentication thư mục được bảo vệ?

Có những cách giải quyết có thể đạt được điều này, nhưng chúng có khả năng nguy hiểm vì chúng có thể có lỗi hoặc không hoạt động trong một số tình huống / trình duyệt nhất định. Đó là lý do tại sao tôi đang tìm kiếm giải pháp chính xác và sạch sẽ.


Vui lòng xác định mục đích cho đăng xuất của bạn. Đây có nên là một đăng xuất bắt buộc (hủy kích hoạt người dùng)? Chức năng đăng xuất đơn giản cho người dùng? Còn gì nữa không?
Karsten

6
Tôi không hiểu tại sao điều này lại quan trọng, nhưng đó là cả hai trường hợp: hủy kích hoạt dựa trên các điều kiện nội bộ trong ứng dụng cũng như nút đăng xuất thông thường. Hãy giải thích tại sao nó quan trọng, tôi sẽ chỉnh sửa nó trực tiếp vào câu hỏi.
Josef Sábl

2
"Giải pháp chính xác và sạch sẽ" là các trình duyệt có nút đăng xuất riêng, khi được nhấp, sẽ khiến trình duyệt ngừng gửi tiêu đề Auth ... Mọi người có thể mơ, phải không?
DanMan

1
Thanh công cụ dành cho nhà phát triển web có "nút" như vậy.
Josef Sábl

Josef đã nói gì: thanh công cụ dành cho nhà phát triển web cho Firefox ->Miscellaneous -> Clear Private Data -> HTTP Authentication
Yarin

Câu trả lời:


103

Mu. Không có cách chính xác nào tồn tại , thậm chí không có cách nào phù hợp trên các trình duyệt.

Đây là một vấn đề xuất phát từ đặc tả HTTP (phần 15.6):

Các máy khách HTTP và tác nhân người dùng hiện tại thường lưu giữ thông tin xác thực vô thời hạn. HTTP / 1.1. không cung cấp một phương thức cho một máy chủ để hướng khách hàng loại bỏ các thông tin được lưu trong bộ nhớ cache này.

Mặt khác, mục 10.4.2 nói:

Nếu yêu cầu đã bao gồm thông tin xác thực ủy quyền, thì phản hồi 401 cho biết rằng ủy quyền đã bị từ chối cho các thông tin đăng nhập đó. Nếu phản hồi 401 có cùng thách thức với phản hồi trước đó và tác nhân người dùng đã thử xác thực ít nhất một lần, thì người dùng NÊN được trình bày thực thể được đưa ra trong phản hồi, vì thực thể đó có thể bao gồm thông tin chẩn đoán có liên quan.

Nói cách khác, bạn có thể hiển thị lại hộp đăng nhập (như @Karsten nói), nhưng trình duyệt không phải thực hiện yêu cầu của bạn - vì vậy đừng phụ thuộc vào tính năng (mis) này quá nhiều.


9
Đây là một lỗi trong RFC. W3C quá lười để sửa. Buồn quá.
Erik Aronesty

Như @Jonathan Hanson đề xuất bên dưới , bạn có thể sử dụng cookie theo dõi cùng với xác thực HTTP. Đây là phương pháp tốt nhất cho tôi.
máy móc

61

Phương pháp hoạt động độc đáo trong Safari. Cũng hoạt động trong Firefox và Opera, nhưng có cảnh báo.

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

Điều này cho trình duyệt mở URL với tên người dùng mới, ghi đè lên trước đó.


14
Theo RFC 3986 (URI: Cú pháp chung) phần 3.2.1. (Thông tin người dùng) việc sử dụng user:password@hostbị phản đối. Chỉ sử dụng http://logout@yourserver.example.com/không và nên hoạt động trong hầu hết các trường hợp.
aef

1
@andho: vâng, đó là một chuyển hướng. Bạn nên sử dụng nó với trạng thái 302.
Kornel

1
Rõ ràng một liên kết đơn giản đến logout@yourserver.example.com cũng hoạt động (liên kết "ngắt kết nối" với URL này) thay vì chuyển hướng http trong PHP ... có bất kỳ nhược điểm nào không?
moala

4
Hãy coi chừng: Mẫu trình sử dụng đường dẫn tương đối có thể thất bại khi nó được thực hiện sau một tái đăng nhập (login với logout nhắc), bởi vì địa chỉ này vẫn sẽ là logout@yourserver.example.com/path và không yourserver.example.com/path /
Jason

1
logout@yourserver.example.com hoạt động sự cố trắng trong Chrome, nhưng nhắc nhở một vấn đề bảo mật trong Firefox. đăng xuất: true@yourserver.example.com không làm cho Firefox trở thành một vấn đề bảo mật. Cả hai url đều không hoạt động trong IE8: /
Thor A. Pedersen

46

Câu trả lời đơn giản là bạn không thể đăng xuất khỏi xác thực http.

Câu trả lời dài:
Http-auth (giống như phần còn lại của thông số HTTP) có nghĩa là không trạng thái. Vì vậy, "đăng nhập" hoặc "đăng xuất" không thực sự là một khái niệm có ý nghĩa. Cách tốt hơn để xem nó là hỏi, đối với mỗi yêu cầu HTTP (và hãy nhớ tải trang thường là nhiều yêu cầu), "bạn có được phép làm những gì bạn yêu cầu không?". Máy chủ xem mỗi yêu cầu là mới và không liên quan đến bất kỳ yêu cầu nào trước đó.

Các trình duyệt đã chọn ghi nhớ thông tin đăng nhập mà bạn nói với họ trên 401 đầu tiên và gửi lại mà không có sự cho phép rõ ràng của người dùng đối với các yêu cầu tiếp theo. Đây là một nỗ lực nhằm cung cấp cho người dùng mô hình "đăng nhập / đăng xuất" mà họ mong đợi, nhưng đó hoàn toàn là một loại bùn. Đó là trình duyệt mô phỏng trạng thái bền bỉ này. Các máy chủ web là hoàn toàn không biết về nó.

Vì vậy, "đăng xuất", trong bối cảnh http-auth hoàn toàn là một mô phỏng được cung cấp bởi trình duyệt và do đó nằm ngoài thẩm quyền của máy chủ.

Vâng, có bùn. Nhưng họ phá vỡ RESTful-ness (nếu điều đó có giá trị với bạn) và chúng không đáng tin cậy.

Nếu bạn hoàn toàn yêu cầu mô hình đăng nhập / đăng xuất để xác thực trang web của mình, thì cách tốt nhất là cookie theo dõi, với sự tồn tại của trạng thái được lưu trữ trên máy chủ theo cách nào đó (mysql, sqlite, Flatfile, v.v.). Điều này sẽ yêu cầu tất cả các yêu cầu được đánh giá, ví dụ, với PHP.


26

Giải pháp thay thế

Bạn có thể làm điều này bằng 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>

Những gì được thực hiện ở trên là:

  • cho IE - chỉ cần xóa bộ đệm auth và chuyển hướng ở đâu đó

  • đối với các trình duyệt khác - gửi một XMLHttpRequest đằng sau hậu trường với tên đăng nhập và mật khẩu 'đăng xuất'. Chúng tôi cần gửi nó đến một số đường dẫn sẽ trả lại 200 OK cho yêu cầu đó (nghĩa là nó không yêu cầu xác thực HTTP).

Thay thế '/where/to/redirect'bằng một số đường dẫn để chuyển hướng đến sau khi đăng xuất và thay thế '/path/that/will/return/200/OK'bằng một số đường dẫn trên trang web của bạn sẽ trả lại 200 OK.


5
Đó là một cách giải quyết để đăng nhập như một người dùng khác. Nhưng điều này thực sự hoạt động và xứng đáng tín dụng hơn.
Charlie Rudenstål

2
Tôi nghĩ rằng đây là câu trả lời tốt nhất. Như đã nêu trong câu trả lời này cho một câu hỏi tương tự, có thể có một số lợi thế để ngẫu nhiên hóa mật khẩu.
zelanix

2
Đây là những gì tôi muốn - làm việc trong tất cả các trình duyệt không có vấn đề. Giữ trang "đăng xuất" mà tôi được thừa kế nguyên vẹn. Tôi không nhất thiết muốn sử dụng JS (có thể là phi lý), nhưng tất cả các câu trả lời khác đều có vấn đề trên trình duyệt chéo và điều này hoạt động hoàn hảo.
dgig

Tôi không thể làm cho công việc này theo cách được giải thích. Khi tôi quay lại khu vực được bảo mật, trình duyệt sẽ tự xác thực lại bằng cách gửi thông tin xác thực được sử dụng cuối cùng trong tiêu đề. Tuy nhiên, với một chút thay đổi, nó đã làm việc cho tôi. Tôi đã thay đổi phản hồi 200 OK với một tiêu đề có cùng Vương quốc của khu vực được bảo mật, nhưng chỉ chấp nhận người dùng / đăng xuất "logout: logout". Theo cách này, người dùng đã đăng nhập với người dùng "đăng xuất" này và đây là người dùng thử lại khi anh ta quay lại khu vực được bảo mật. Khu vực bảo mật từ chối người dùng / vượt qua này, vì vậy người dùng có thể thay đổi thông tin đăng nhập.
jonaguera 17/2/2015

2
Điều này không hoạt động như nó được giải thích. Đã thử nghiệm trong Chrome 40 và Firefox 35.
funforums

13

Giải pháp thay thế (không phải là giải pháp sạch, đẹp (hoặc thậm chí hoạt động! Xem bình luận)):

Vô hiệu hóa thông tin của anh ấy một lần.

Bạn có thể di chuyển logic xác thực HTTP của mình sang PHP bằng cách gửi các tiêu đề thích hợp (nếu chưa đăng nhập):

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

Và phân tích cú pháp đầu vào bằng:

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

Vì vậy, vô hiệu hóa thông tin đăng nhập của anh ta một lần là chuyện nhỏ.


18
Vấn đề với giải pháp này là: Bạn cho IE biết rằng thông tin đăng nhập không ổn. Nó hiển thị hộp thoại đăng nhập với các trường trống (không hiển thị các giá trị được lưu trong trình quản lý mật khẩu). Nhưng khi bạn bấm hủy và làm mới trang, nó sẽ gửi thông tin lưu trữ, do đó đăng nhập lại.
Josef Sábl

Xuống cấp; Giống như Josef Sable nhận xét, điều này không giải quyết vấn đề trong tầm tay.
Chris Wesseling

7

Đăng xuất khỏi HTTP Basic Auth theo hai bước

Giả sử tôi có một lĩnh vực xác thực cơ bản HTTP có tên là Mật khẩu được bảo vệ mật khẩu và Bob đã đăng nhập. Để đăng xuất tôi thực hiện 2 yêu cầu AJAX:

  1. Truy cập tập lệnh / logout_step1. Nó thêm một người dùng tạm thời ngẫu nhiên vào .htusers và phản hồi với thông tin đăng nhập và mật khẩu của nó.
  2. Truy cập tập lệnh / logout_step2 được xác thực bằng thông tin đăng nhập và mật khẩu tạm thời của người dùng . Kịch bản xóa người dùng tạm thời và thêm tiêu đề này vào phản hồi:WWW-Authenticate: Basic realm="Password protected"

Tại thời điểm này, trình duyệt đã quên thông tin đăng nhập của Bob.


1
Ồ Điều này thực sự xứng đáng +1 cho sự sáng tạo tuyệt đối, ngay cả khi đó là một điều hoàn toàn hạt dẻ phải làm.
Andy Triggs

7

Giải pháp của tôi cho vấn đề là như sau. Bạn có thể tìm thấy chức năng http_digest_parse, $realm$userstrong ví dụ thứ hai của trang này: 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

Thông thường, một khi trình duyệt đã yêu cầu người dùng xác thực và cung cấp chúng cho một trang web cụ thể, nó sẽ tiếp tục làm như vậy mà không cần nhắc thêm. Không giống như các cách khác nhau mà bạn có thể xóa cookie ở phía máy khách, tôi không biết một cách tương tự để yêu cầu trình duyệt quên thông tin xác thực được cung cấp.


Tôi tin rằng có một tùy chọn để xóa các phiên xác thực khi bạn chọn "Xóa dữ liệu riêng tư" trong Firefox
Kristian J.

1
Ngoài ra tiện ích mở rộng Thanh công cụ dành cho nhà phát triển Web cho Firefox cung cấp tính năng xóa Xác thực HTTP. Nhưng điều này không có vấn đề gì vì chúng tôi thực sự không thể yêu cầu người dùng tải xuống các tiện ích mở rộng FF hoặc chạy các lệnh trình duyệt khó hiểu :-)
Josef Sábl 3/03/2016

2
Cách đăng xuất mặc định của HTTP ra khỏi HTTP auth có sẵn trong "Công cụ"> "Xóa lịch sử gần đây ...", dưới dạng hộp kiểm "Đăng nhập hoạt động". Điều này không trực quan và cũng không cho phép bạn chỉ đăng xuất khỏi một tên miền, bạn luôn luôn đăng xuất khỏi mỗi trang.
aef

2

Trac - theo mặc định - cũng sử dụng Xác thực HTTP. Thoát ra không hoạt động và không thể được sửa chữa:

  • Đây là một vấn đề với chính lược đồ xác thực HTTP và chúng tôi không thể làm gì trong Trac để sửa nó.
  • Hiện tại không có cách giải quyết (JavaScript hoặc khác) hoạt động với tất cả các trình duyệt chính.

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

Có vẻ như không có câu trả lời nào cho câu hỏi, vấn đề đó đã được báo cáo bảy năm trước và nó có ý nghĩa hoàn hảo: HTTP là không trạng thái. Yêu cầu được thực hiện với thông tin xác thực hoặc không. Nhưng đó là vấn đề của khách hàng gửi yêu cầu, không phải máy chủ nhận được yêu cầu. Máy chủ chỉ có thể nói nếu URI yêu cầu có cần ủy quyền hay không.


2

Tôi cần thiết lập lại ủy quyền .htaccess vì vậy tôi đã sử dụng điều này:

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

Tìm thấy nó ở đây: http://php.net/manual/en/features.http-auth.php

Đi hình.

Một số giải pháp nằm trên trang đó và thậm chí nó còn ghi chú ở phía dưới: Lynx, không xóa auth như các trình duyệt khác;)

Tôi đã thử nghiệm nó trên các trình duyệt đã cài đặt của mình và sau khi đóng, mỗi trình duyệt có vẻ như nó luôn đòi hỏi phải thử lại.


Điều này dường như không hoạt động, tôi nhận được văn bản hủy mà không có hộp đăng nhập bật lên.
Michael

Hóa ra, việc gửi WWW-Authenticateđã gây ra vấn đề, tự động thoát khỏi đó đã đăng xuất tôi.
Michael

Và ngược lại, dường như việc KHÔNG gửi WWW-Authenticatetrong khi khắc phục sự cố trong một trình duyệt (Chrome) khiến một trình duyệt khác (Firefox) ghi nhớ thông tin đăng nhập và gửi chúng theo yêu cầu tiếp theo, dẫn đến đăng nhập lại tự động! Argh!
Michael

Sau đó nhìn vào UA và thực hiện cái này hay cái kia có vẻ giống như một giải pháp
Lennart Rolland

2

Đây có thể không phải là giải pháp được tìm kiếm nhưng tôi đã giải quyết nó như thế này. tôi có 2 tập lệnh cho quá trình đăng xuất.

đăng xuất

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

log.php

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

Bằng cách này, tôi không nhận được cảnh báo và phiên của tôi bị chấm dứt


1
Đây là giải pháp duy nhất thực sự hiệu quả với tôi! Đã thử nghiệm trên Firefox 37 và Chromium 41
zesaver

1

AFAIK, không có cách rõ ràng nào để thực hiện chức năng "đăng xuất" khi sử dụng xác thực htaccess (tức là dựa trên HTTP).

Điều này là do xác thực như vậy sử dụng mã lỗi HTTP '401' để thông báo cho trình duyệt rằng thông tin đăng nhập là bắt buộc, tại đó trình duyệt sẽ nhắc người dùng biết chi tiết. Từ đó trở đi, cho đến khi trình duyệt được đóng, nó sẽ luôn gửi thông tin đăng nhập mà không cần nhắc thêm.


1

Giải pháp tốt nhất tôi tìm thấy cho đến nay là (đó là loại mã $isLoggedIngiả, biến giả là http auth):

Tại thời điểm "đăng xuất", chỉ cần lưu trữ một số thông tin cho phiên nói rằng người dùng thực sự đã đăng xuất.

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

Ở nơi tôi kiểm tra xác thực, tôi mở rộng điều kiện:

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

Phiên có phần nào được liên kết với trạng thái xác thực http để người dùng vẫn đăng xuất miễn là anh ta giữ trình duyệt mở và miễn là xác thực http vẫn tồn tại trong trình duyệt.


4
Trong khi xác thực cơ bản http là RESTful, phiên không.
deamon

1

Có lẽ tôi đang thiếu điểm.

Cách đáng tin cậy nhất mà tôi đã tìm thấy để kết thúc Xác thực HTTP là đóng trình duyệt và tất cả các cửa sổ trình duyệt. Bạn có thể đóng cửa sổ trình duyệt bằng Javascript nhưng tôi không nghĩ bạn có thể đóng tất cả các cửa sổ trình duyệt.


fyi một số trình duyệt sẽ không đóng cửa sổ nếu đó là tab duy nhất mở, vì vậy vấn đề thực sự là tranh luận
scape

Cách đây nhiều năm, tôi có nhiệm vụ thực hiện nút đăng xuất mà không đóng cửa sổ :-) Nhưng có lẽ họ sẽ không dừng lại ở việc "không đóng cửa sổ". Nhưng này, đây là giải pháp đơn giản có thể hiệu quả với ai đó và sau đó tôi đã bỏ lỡ nó.
Josef Sábl

1

Cách hiệu quả duy nhất tôi tìm thấy để xóa sạch thông tin AND PHP_AUTH_DIGESThoặc là gọi tiêu đề .PHP_AUTH_USERPHP_AUTH_PWHTTP/1.1 401 Unauthorized

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

0

Mặc dù những người khác đều đúng khi nói rằng không thể đăng xuất khỏi xác thực http cơ bản, có nhiều cách để thực hiện xác thực có hành vi tương tự. Một appeoach rõ ràng là sử dụng auth_memcookie . Nếu bạn thực sự muốn triển khai xác thực HTTP cơ bản (nghĩa là sử dụng hộp thoại trình duyệt để đăng nhập trather hơn dạng HTTP) bằng cách này - chỉ cần đặt xác thực thành thư mục được bảo vệ .htaccess có chứa tập lệnh PHP để chuyển hướng trở lại nơi người dùng đến sau tạo phiên memcache.


0

Có rất nhiều câu trả lời hay - phức tạp ở đây. Trong trường hợp cụ thể của tôi, tôi tìm thấy một sửa chữa đơn giản và sạch sẽ cho đăng xuất. Tôi vẫn chưa thử nghiệm trong Edge. Trên trang mà tôi đã đăng nhập, tôi đã đặt một liên kết đăng xuất tương tự như sau:

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

Và trong phần đầu của trang logout.html đó (cũng được bảo vệ bởi .htaccess) Tôi có một trang làm mới tương tự như sau:

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

Nơi bạn sẽ để lại từ "đăng xuất" để xóa tên người dùng và mật khẩu được lưu trong bộ nhớ cache cho trang web.

Tôi sẽ thừa nhận rằng nếu nhiều trang cần thiết để có thể đăng nhập trực tiếp từ đầu, mỗi điểm trong số đó sẽ cần trang logout.html tương ứng của riêng họ. Nếu không, bạn có thể tập trung đăng xuất bằng cách giới thiệu một bước gác cổng bổ sung vào quy trình trước dấu nhắc đăng nhập thực tế, yêu cầu nhập cụm từ để đến đích đăng nhập.


1
Khi di chuyển về phía trước, điều này hoạt động, nó sẽ đăng xuất, nhưng lịch sử trở lại của trình duyệt vẫn có thể thiết lập lại phiên.
johnwayne
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.