HTTP-Authentifizierung mit PHP
Die HTTP-Authentifizierung durch PHP ist nur verfügbar,
wenn PHP als Apache-Modul läuft und funktioniert daher nicht mit der
CGI-Version. In einem PHP-Skript für ein Apache-Modul kann man die Funktion
header() benutzen, um die Nachricht "Authentifizierung
notwendig" an den Client-Browser zu senden, damit dieser ein Fenster zur
Eingabe von Benutzername/Passwort öffnet. Hat der Benutzer diese
eingegeben, wird die URL des PHP-Scripts mit den vordefinierten Variablen
PHP_AUTH_USER, PHP_AUTH_PW und
AUTH_TYPE, die den Benutzernamen, das Passwort und den
Typ der Authentifizierung enthalten, erneut aufgerufen. Diese
vordefinierten Variablen befinden sich in den Arrays $_SERVER und
$HTTP_SERVER_VARS. Es werden sowohl die "Basic"- als
auch (seit PHP 5.1.0) die "Digest"-Authentifizierungsmethode unterstützt.
Näheres hierzu bei der header() Funktion.
Hinweis:
Anmerkung zur PHP Version
Superglobale,
wie $_SERVER, sind seit
PHP
4.1.0 verfügbar.
Ein Auszug aus einem Skript, das die Clientauthentifizierung auf einer
Seite erzwingt, würde so aussehen:
Beispiel #1 Beispiel: Basic-HTTP-Authentifizierung
<?php
if (!isset($_SERVER['PHP_AUTH_USER'])) {
header('WWW-Authenticate: Basic realm="My Realm"');
header('HTTP/1.0 401 Unauthorized');
echo 'Text, der gesendet wird, falls der Benutzer auf Abbrechen drückt';
exit;
} else {
echo "<p>Hallo {$_SERVER['PHP_AUTH_USER']}.</p>";
echo "<p>Sie gaben {$_SERVER['PHP_AUTH_PW']} als Passwort ein.</p>";
}
?>
Beispiel #2 Beispiel: Digest-HTTP-Authentifizierung
In diesem Beispiel wird gezeigt, wie ein einfaches Skript für die
Digest-HTTP-Authentifizierung implementiert wird. Weitere Informationen
dazu finden Sie im
RFC 2617.
<?php
$realm = 'Geschützter Bereich';
// Benutzer => Passwort
$benutzer = array('admin' => 'mypass', 'gast' => 'gast');
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('Text, der gesendet wird, falls der Benutzer auf Abbrechen drückt');
}
// Analysieren der Variable PHP_AUTH_DIGEST
if (!($daten = http_digest_parse($_SERVER['PHP_AUTH_DIGEST'])) ||
!isset($benutzer[$daten['username']]))
die('Falsche Zugangsdaten!');
// Erzeugen einer gültigen Antwort
$A1 = md5($daten['username'] . ':' . $realm . ':' .
$benutzer[$daten['username']]);
$A2 = md5($_SERVER['REQUEST_METHOD'] . ':' . $daten['uri']);
$gueltige_antwort = md5($A1 . ':' . $daten['nonce'] . ':' . $daten['nc'] .
':' . $daten['cnonce'] . ':' . $daten['qop'] . ':' .
$A2);
if ($daten['response'] != $gueltige_antwort)
die('Falsche Zugangsdaten!');
// OK, gültige Benutzername & Passwort
echo 'Sie sind angemeldet als: ' . $daten['username'];
// Funktion zum analysieren der HTTP-Auth-Header
function http_digest_parse($txt) {
// gegen fehlende Daten schützen
$noetige_teile = array('nonce'=>1, 'nc'=>1, 'cnonce'=>1, 'qop'=>1,
'username'=>1, 'uri'=>1, 'response'=>1);
$daten = array();
preg_match_all('@(\w+)=(?:([\'"])([^\2]+)\2|([^\s,]+))@', $txt, $treffer,
PREG_SET_ORDER);
foreach ($treffer as $t) {
$daten[$t[1]] = $t[3] ? $t[3] : $t[4];
unset($noetige_teile[$t[1]]);
}
return $noetige_teile ? false : $daten;
}
?>
Hinweis:
Hinweis zur Kompatibilität
Achten Sie für die maximale Kompatibilität mit allen Clients bei den
Header-Zeilen auf die richtige Schreibweise! Das Schlüsselwort "Basic"
sollte genau so geschrieben werden, der Realm-String muss in doppelte
(nicht einfache) Anführungszeichen gesetzt werden und in der
HTTP/1.0 401-Zeile darf nur genau ein Leerzeichen vor
dem 401-Code stehen. Wie im Beispiel oben zu sehen,
müssen die Authentifizierungsparameter durch Kommata getrennt werden.
Anstatt PHP_AUTH_USER und PHP_AUTH_PW
wie im Beispiel oben einfach nur auszugeben, werden Sie den Benutzernamen
und das Passwort auf Gültigkeit überprüfen wollen. Dies kann durch abfragen
einer Datenbank oder die Suche des Benutzers in einer Textdatei geschehen.
Vorsicht bei einigen Internet Explorer-Versionen - sie scheinen sehr
wählerisch zu sein, was die Reihenfolge der Header angeht. Abhilfe schafft
hier das Senden des WWW-Authenticate-Headers vor dem
HTTP/1.0 401-Header.
Um zu unterbinden, dass ein Skript das Passwort einer durch einen
traditionellen externen Mechanismus geschützten Seite ausliest, werden die
PHP_AUTH Variablen ab PHP 4.3.0 nicht gesetzt, wenn eine externe
Authentifizierung für diese bestimmte Seite und Safe Mode aktiviert sind.
In diesem Fall kann die Variable REMOTE_USER benutzt
werden, um den Benutzer durch die externe Zugriffskontrolle zu
identifizieren. Es kann also $_SERVER['REMOTE_USER']
benutzt werden.
Hinweis:
Konfigurationshinweis
PHP prüft das Vorhandensein einer AuthType
Apache-Direktive, um zu entscheiden, ob eine externe Authentifizierung
aktiv ist.
Zu beachten ist, dass obenstehendes keinesfalls jemanden, der auf dem
selben Server die Kontrolle über eine nicht authentifizierte URL hat, davon
abhalten kann, Passwörter von authentifizierten URLs auszulesen.
Sowohl Netscape als auch der Internet Explorer löschen den
lokalen Authentifizierungscache des Browserfensters, wenn der
Server eine 401-Meldung zurückgibt. Dies kann benutzt werden, um
einen Benutzer "auszuloggen" und eine erneute Eingabe des
Benutzernamens/Passworts zu erzwingen. Manchmal wird dieses Verhalten
für das automatische Ausloggen nach Ablauf einer bestimmten Zeitspanne
oder für einen Logout-Button genutzt.
Beispiel #3
HTTP-Authentifizierung, mit erneuter Anforderung von Name/Passwort
<?php
function authentifizieren() {
header('WWW-Authenticate: Basic realm="Test Authentication System"');
header('HTTP/1.0 401 Unauthorized');
echo "Bitte geben Sie eine gültige Login-ID und das Passwort für den
Zugang ein\n";
exit;
}
if (!isset($_SERVER['PHP_AUTH_USER']) ||
($_POST['Gesehen'] == 1 &&
$_POST['AlteAuth'] == $_SERVER['PHP_AUTH_USER'])) {
authentifizieren();
} else {
echo "<p>Willkommen: {$_SERVER['PHP_AUTH_USER']}<br />";
echo "Alt: {$_REQUEST['AlteAuth']}";
echo "<form action='{$_SERVER['PHP_SELF']}' METHOD='post'>\n";
echo "<input type='hidden' name='Gesehen' value='1' />\n";
echo "<input type='hidden' name='AlteAuth'
value='{$_SERVER['PHP_AUTH_USER']}' />\n";
echo "<input type='submit' value='Erneut Anmelden' />\n";
echo "</form></p>\n";
}
?>
Dieses Verhalten wird vom Authentifizierungsstandard HTTP Basic nicht
gefordert, daher sollte man sich nie darauf verlassen. Tests mit Lynx haben
gezeigt, dass Lynx die Authentifizierungsinformationen bei Erhalt einer
401-Meldung nicht löscht. Solange der benötigte Berechtigungsnachweis nicht
geändert wird, kann der Benutzer auf die vorhergehende Seite zurück und
danach vorwärts und die angeforderte Adresse wieder öffnen. Der Benutzer
kann die Authentifizierungsinformationen aber durch Drücken von '_'
löschen.
Weiterhin muss beachtet werden, dass die HTTP-Authentifizierung vor PHP
4.3.3 unter dem Microsoft IIS mit der CGI-Version von PHP aufgrund einer
Einschränkung des IIS nicht funktioniert. Damit sie mit PHP 4.3.3+
funktioniert, müssen Sie die "Directory Security" Ihrer
IIS-Konfiguration ändern. Klicken Sie auf "Edit" und
aktivieren Sie nur "Anonymous Access". Alle anderen
Felder sollten deaktiviert bleiben.
Eine andere Einschränkung gibt es, falls Sie das IIS-Modul (ISAPI) und PHP
4 verwenden: Sie können nicht die PHP_AUTH_*-Variablen
benutzen, aber stattdessen steht die Variable
HTTP_AUTHORIZATION zur Verfügung. Schauen Sie sich dazu
z.B. folgenden Code an: list($user, $pw) = explode(':',
base64_decode(substr($_SERVER['HTTP_AUTHORIZATION'], 6)));
Hinweis:
IIS-Anmerkung:
Damit die HTTP-Authentifizierung mit dem IIS funktioniert, muss die
PHP-Konfigurationsanweisung cgi.rfc2616_headers auf
0 (die Voreinstellung) gesetzt sein.
Hinweis:
Falls safe mode aktiviert ist, wird
die uid des Skripts dem realm-Teil des
WWW-Authenticate-Headers hinzugefügt.