Podręcznik PHP
Poprzedni |  | Następny

# Rozdział 34. Uwierzytelnianie HTTP w PHP

Uwierzytelnianie HTTP jest obsługiwana przez PHP tylko wtedy, gdy PHP pracuje jako moduł Apache'a, nie jest dostępna w trybie CGI. W skrypcie można użyć funkcji header() by wysłać do przeglądarki komunikat "Wymagana autoryzacja", co spowoduje wyświetlenie okienka z polami Użytkownik i Hasło. Po wypełnieniu przez użytkownika tych pól, URL zawierający skrypt PHP zostanie ponownie wywołany z ustawionymi predefiniowanymi zmiennymi `PHP_AUTH_USER`, `PHP_AUTH_PW` i `AUTH_TYPE` zawierającymi odpowiednio nazwę użytkownika, hasło i typ autoryzacji. Zmienne te będą dostępne w tablicach $_SERVER oraz `$HTTP_SERVER_VARS`. Obecnie obsługiwane są autoryzacje typu "Basic" i "Digest" (od PHP 5.1.0). Więcej informacji znajdziesz w opisie funkcji header().

> PHP Version Note: Zmienne superglobalne, takie jak $_SERVER, udostępniono w PHP 4.1.0. `$HTTP_SERVER_VARS` są dostępne od PHP 3.

Przykładowy skrypt wymuszający autoryzację klienta:

Przykład 34-1. Uwierzytelnianie Basic HTTP

`<?php if (!isset($_SERVER['PHP_AUTH_USER'])) { header('WWW-Authenticate: Basic realm="My Realm"'); header('HTTP/1.0 401 Unauthorized'); echo 'Tekst do wysłania, jeśli użytkownik wciśnie przycisk Anuluj'; exit; } else { echo "<p>Hej {$_SERVER['PHP_AUTH_USER']}.</p>"; echo "<p>Twoje hasło to {$_SERVER['PHP_AUTH_PW']}.</p>"; } ?>`

Przykład 34-2. Przykład uwierzytelnianie Digest HTTP

Ten przykład pokazuje jak zaimplementować proste umierzytelnianie Digest HTTP. Po więcej informacji przeczytaj RFC 2617.

`<?php
$realm = 'Zastrzeżona strefa';
//user => password
$users = array('admin' => 'mypass', 'guest' => 'guest');
if (empty($_SERVER['PHP_AUTH_DIGEST'])) {
header('HTTP/1.1 401 Unauthorized');
header('WWW-Authenticate: Digest realm="'.$realm.
'",qop="auth",nonce="'.uniqid().'",opaque="'.md5($realm).'"');
die('Tekst do wysłania kiedy użytkownik kliknie klawisz anuluj');
}
// analiza zmiennej PHP_AUTH_DIGEST
if (!($data = http_digest_parse($_SERVER['PHP_AUTH_DIGEST'])) ||
!isset($users[$data['username']]))
die('Nieprawidłowe listy uwierzytelniające!');
// tworzenie prawidłowej odpowiedzi
$A1 = md5($data['username'] . ':' . $realm . ':' . $users[$data['username']]);
$A2 = md5($_SERVER['REQUEST_METHOD'].':'.$data['uri']);
$valid_response = md5($A1.':'.$data['nonce'].':'.$data['nc'].':'.$data['cnonce'].':'.$data['qop'].':'.$A2);
if ($data['response'] != $valid_response)
die('Nieprawidłowe listy uwierzytelniające!');
// ok, prawidłowa nazwa użytkownika i hasło
echo 'Jesteś zalogowany jako: ' . $data['username'];
// function to parse the http auth header
function http_digest_parse($txt)
{
// zabezpieczenie przeciwko brakującym informacjom
$needed_parts = array('nonce'=>1, 'nc'=>1, 'cnonce'=>1, 'qop'=>1, 'username'=>1, 'uri'=>1, 'response'=>1);
$data = array();
preg_match_all('@(\w+)=([\'"]?)([a-zA-Z0-9=./\_-]+)\2@', $txt, $matches, PREG_SET_ORDER);
foreach ($matches as $m) {
$data[$m[1]] = $m[3];
unset($needed_parts[$m[1]]);
}
return $needed_parts ? false : $data;
}
?>`

> Kompatybilność: Należy uważać z linijkami dodawanymi do nagłówka HTTP. W celu zachowania maksymalnej zgodności ze wszystkimi klientami, słowo Basic powinno zaczynać się dużą literą "B", wartość realm powinna być otoczona cudzysłowami (nie apostrofami), i dokładnie jeden znak odstępu powinien poprzedzać kod 401 w linii HTTP/1.0 401. Parametry autoryzacyjne muszą być oddzielone przecinkami jak pokazano to w przykładzie digest powyżej.

Zamiast wyświetlać wartość `PHP_AUTH_USER` i `PHP_AUTH_PW`, jak to zrobiono w powyższym przykładzie, zechcesz zapewne sprawdzić poprawność nazwy użytkownika i hasła. Na przykład poprzez zapytanie do bazy danych lub odnalezienie użytkownika w pliku dbm.

Należy uważać na kapryśne przeglądarki Internet Explorer. Są wrażliwe na kolejność wysyłanych nagłówków HTTP. Wysłanie nagłowka WWW-Authenticate przed `HTTP/1.0 401` powinno rozwiązać problem.

Aby zapobiec sytuacji w której ktoś napisze skrypt wykradający hasło wysłane tradycyjnym zewnętrznym mechanizmem, zmienne PHP_AUTH nie będą ustawiane, jeśli dla danej strony aktywna jest autoryzacja zewnętrzna i tryb bezpieczny jest włączony. Bez względu na to, `REMOTE_USER` może zostać użyte do zidentyfikowania użytkownika autoryzowanego zewnętrznie Zatem, możesz użyć `$_SERVER['REMOTE_USER']`.

> Konfiguracja: Aby wykryć czy miała miejsce zewnętrzna autoryzacja, PHP sprwadza obecność dyrektywy `AuthType`.

Powyższa metoda nie zapobiega jednak wykradaniu haseł do stron wymagających autoryzacji przez kogoś, kto na tym samym serwerze kontroluje strony nie wymagające autoryzacji.

Zarówno Netscape Navigator jak i Internet Explorer opróżnią bufor autoryzacji po otrzymaniu od serwera kodu 401. Można w ten sposób wylogowanić użytkownika i zmusić go do ponownego wysłania nazwy użytkownika i hasła. Tej metody można użyć do wylogowania użytkownika po określonym czasie lub stworzenia przycisku "Wyloguj".

Przykład 34-3. Uwierzytelnianie HTTP z wymuszeniem przelogowania

`<?php
function authenticate() {
header('WWW-Authenticate: Basic realm="Testowy system autoryzacji"');
header('HTTP/1.0 401 Unauthorized');
echo "Musisz podać poprawny login i hasło by wejść na tę stronę\n";
exit;
}
if (!isset($_SERVER['PHP_AUTH_USER']) ||
($_POST['SeenBefore'] == 1 && $_POST['OldAuth'] == $_SERVER['PHP_AUTH_USER'])) {
authenticate();
} else {
echo "<p>Witaj: {$_SERVER['PHP_AUTH_USER']}<br />";
echo "Poprzednio: {$_REQUEST['OldAuth']}";
echo "<form action='{$_SERVER['PHP_SELF']}' METHOD='post'>\n";
echo "<input type='hidden' name='SeenBefore' value='1' />\n";
echo "<input type='hidden' name='OldAuth' value='{$_SERVER['PHP_AUTH_USER']}' />\n";
echo "<input type='submit' value='Re Authenticate' />\n";
echo "</form></p>\n";
}
?>`

Powyższa metoda nie jest wymagana przez autoryzację HTTP typu "Basic", więc nie można na niej polegać. Testy z przeglądarką Lynx pokazały, że Lynx nie usuwa danych o autoryzacji po odebraniu od serwera kodu 401, zatem przejście wstecz a następnie do przodu otworzy stronę, chyba, że wymagania co do danych autoryzacji zmieniły się. Użytkownik może jednak użyć klawisza '_' by usunąc dane o autoryzacji.

Zwóć uwagę, że do wersji PHP 4.3.3, Autoryzacja HTTP nie działała na serwerze Microsoft IIS z PHP w wersji CGI z powodu ograniczeń IIS. Aby zmusić go do działania w PHP 4.3.3+ musisz wyedytować "Bezpieczeństwo katalogów" w konfiguracji IIS. Kliknij na "Edytuj" i zaznacz wyłącznie "Anonimowy Dostęp", wszystkie inne pola powinny pozostać nie zaznaczone.

Inne ogranicznie jest jeśli używasz modułu IIS (ISAPI) i PHP 4, nie możesz wtedy użyć zmiennych `PHP_AUTH_*` ale zamiast nich, dostępna jest zmienna `HTTP_AUTHORIZATION`. Na przykład rozważ następujący kod: `list($user, $pw) = explode(':', base64_decode(substr($_SERVER['HTTP_AUTHORIZATION'], 6)));`

> Notatka IIS:: Aby Autoryzacja HTTP działała z IIS, dyrektywa PHP cgi.rfc2616_headers musi być ustawiona na `0` (domyślna wartość).

> Notatka: Jeśli włączony jest tryb bezpieczny, uid skryptu jest doklejany do pola `realm` nagłówka `WWW-Authenticate`.

Poprzedni | Spis treści | Następny
Możliwości | Początek rozdziału | Ciasteczka (cookies)