Wenn man Teile eines Internetauftritts vor neugierigen Augen schützen will, benötigt man ein wirksames System zur Zugangskontrolle. In vielen Fällen bietet sich hier die bekannte HTTP-Authentifizierung an, die man beispielsweise per .htaccess-Datei umsetzen kann. Diese Methode hat aber den Nachteil, dass es auch in modernen Browsern immer noch keine praktikable Möglichkeit gibt, sich wieder abzumelden. Dieser Artikel soll zeigen, wie man Dokumente auf relativ einfache Weise sinnvoll schützen kann. Voraussetzung dafür ist ein Webserver mit PHP-Unterstützung.
Das System wird mittels Sessions umgesetzt (engl. Session = Sitzung). In PHP ist eine Session im Prinzip eine Datei, die sich im Dateisystem des Webservers befindet. In ihr sind Daten gespeichert, auf die man über den speziellen Array $_SESSION zugreifen kann. Jeder Benutzer, der mit seinem Browser eine Seite aufruft, die Sessions benutzt, bekommt nun vom Server eine einmalige Session-ID zugewiesen, über die er identifiziert werden kann. Dadurch ist es möglich, Daten benutzerbezogen zu speichern, die während einer Sitzung wiederverwendet werden können. Anwendungsmöglichkeiten sind zum Beispiel ein Warenkorb beim Einkaufen im Internet oder eine Administrationsoberfläche für ein Content Management System.
Das hier beschriebene Loginsystem umfasst die 3 Dateien login.php, logout.php und auth.php, welche sich alle im selben Verzeichnis befinden. Die Datei login.php beinhaltet sowohl das Formular zum Anmelden als auch die Routinen, um die Benutzerdaten zu verarbeiten und bei erfolgreicher Anmeldung entsprechende Daten in die Session zu speichern. Die Datei logout.php zerstört die Sitzungsdaten und meldet so den Benutzer ab. auth.php enthält den essentiellen Teil, nämlich die Überprüfung, ob der Benutzer aktuell angemeldet ist und somit berechtigt, das angeforderte Dokument anzusehen. Diese Datei wird in jedes zu schützende Dokument eingebunden.
die login.php
PHP-Code:
<?php
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
session_start();
$username = $_POST['username'];
$passwort = $_POST['passwort'];
$hostname = $_SERVER['HTTP_HOST'];
$path = dirname($_SERVER['PHP_SELF']);
// Benutzername und Passwort werden überprüft
if ($username == 'benjamin' && $passwort == 'geheim') {
$_SESSION['angemeldet'] = true;
// Weiterleitung zur geschützten Startseite
if ($_SERVER['SERVER_PROTOCOL'] == 'HTTP/1.1') {
if (php_sapi_name() == 'cgi') {
header('Status: 303 See Other');
}
else {
header('HTTP/1.1 303 See Other');
}
}
header('Location: http://'.$hostname.($path == '/' ? '' : $path).'/index.php');
exit;
}
}
?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="de" lang="de">
<head>
<title>Geschützter Bereich</title>
</head>
<body>
<form action="login.php" method="post">
Username: <input type="text" name="username" /><br />
Passwort: <input type="password" name="passwort" /><br />
<input type="submit" value="Anmelden" />
</form>
</body>
</html>
Erläuterung:
Der Programmcode in dieser Datei ist für den Anmeldeprozess zuständig. Das Skript prüft zuerst, ob der Benutzer schon angemeldet ist. Ist das nicht der Fall, wird ein Formular angezeigt, welches Benutzername und Passwort über die Methode POST wieder an die Datei login.php sendet, um dann wiederum weiterverarbeitet zu werden. Hier sind sowohl Benutzername als auch Passwort im Quelltext fest einprogrammiert – denkbar wäre aber zum Beispiel auch die Abfrage von Benutzerdaten aus einer Datenbank oder einer Textdatei, so dass mehrere Benutzernamen eingerichtet werden können.
Wurden Benutzername und Passwort korrekt eingegeben, wird in der am Anfang des Skripts geöffneten Session eine Variable mit dem Namen angemeldet und dem Wert true gespeichert.
Im nächsten Schrit wird der Benutzer auf die Datei index.php weitergeleitet. Dies geschieht mittels der Funktion header('Location: ...'). Da die Location-Anweisung einen vollständigen URI erwartet, man diesen vielleicht aber aus Bequemlichkeit nicht für jedes Projekt ändern will oder den künftigen URI noch nicht kennt, wird er aus Servervariablen zusammengebaut, so dass man nur noch den letzten Teil an seine Bedürfnisse anpassen muss. Im Beispiel befindet sich die Datei index.php im selben Verzeichnis wie login.php.
Waren Benutzername oder Passwort falsch, wird wieder das Login-Formular angezeigt – der Benutzer muss die Zugangsdaten erneut eingeben.
Der HTTP Statuscode 303 See Other wird hier aus Gründen der Standardkonformität gesendet: Bei einem Location-Header sendet PHP standardmäßig den Statuscode 302, nur ist hier nicht definiert, ob der nachfolgende Weiterleitungsrequest per POST oder GET getätigt werden soll. HTTP/1.1 wurde daher unter anderem um die Statuscodes 303 und 307 erweitert, die genau das festlegen. Ältere Browser, die nur HTTP/1.0 implementiert haben, bleiben bei der Abfrage nach $_SERVER['SERVER_PROTOCOL'] außen vor, bekommen so den obligatorischen Statuscode 302 und reagieren darauf, wie sie programmiert wurden. In fast allen dieser älteren Browser wird die nächste Seite dann per GET angefordert.
Wichtig:
Achten Sie darauf, dass die Passwörter gut gesichert sind. Der beste Passwortschutz nützt nichts, wenn Passwörter im Klartext gespeichert sind und im schlimmsten Fall sogar über den Browser gelesen werden können. Auch im Beispiel ist darauf zu achten, dass die Datei login.php nicht für andere Systembenutzer auf dem Webserver lesbar ist. Am bequemsten ist es, wenn eine (z.B. MySQL-)Datenbank mit eigenem Benutzernamen zur Verfügung steht, auf die niemand anderes Zugriff hat. Um zusätzlich an Sicherheit zu gewinnen, kann es außerdem sinnvoll sein, die Passwörter zusätzlich zu verschlüsseln, bzw. nur dessen Hash-Wert zu speichern, beispielsweise per md5-Funktion.
die logout.php
PHP-Code:
<?php
session_start();
session_destroy();
$hostname = $_SERVER['HTTP_HOST'];
$path = dirname($_SERVER['PHP_SELF']);
header('Location: http://'.$hostname.($path == '/' ? '' : $path).'/login.php');
?>
Erläuterung:
Die Datei logout.php ist einzig und allein dafür zuständig, die Session - und damit die Information, dass der Benutzer angemeldet ist - zu zerstören. Danach wird der Benutzer wieder zum Anmeldeformular weitergeleitet.
die auth.php
<?php
session_start();
$hostname = $_SERVER['HTTP_HOST'];
$path = dirname($_SERVER['PHP_SELF']);
if (!isset($_SESSION['angemeldet']) || !$_SESSION['angemeldet']) {
header('Location: http://'.$hostname.($path == '/' ? '' : $path).'/login.php');
exit;
}
?>
Erläuterung:
Dies ist die Datei, die später in jede zu schützende Seite eingebunden werden muss. Es wird dann immer überprüft, ob die Sessionvariable angemeldet existiert und ihr Wert true ist. Falls das nicht der Fall ist, wird der Benutzer wieder zum Anmeldeformular weitergeleitet. Wichtig ist hier die exit-Anweisung nach der Weiterleitung, damit kein weiterer Code ausgeführt werden kann und das Skript sofort beendet wird.
Eure geschützte Datei index.php
PHP-Code:
<?php include('auth.php'); ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="de" lang="de">
<head>
<title>Geschützter Bereich</title>
</head>
<body>
<h1>Herzlichen Glückwunsch!</h1>
<p>Sie sind nun angemeldet.</p>
<p>Sie können sich auch wieder <a href="logout.php">abmelden</a>.</p>
</body>
</html>
Erläuterung:
Die erste und einzige Anweisung, die eine geschützte Seite enthalten muss, ist der Befehl zum Einbinden der Datei auth.php, nämlich include in der ersten Zeile. Der Rest der Datei kann eine ganz normale HTML-Seite sein. Wenn Sie mehrere Dokumente schützen wollen, müssen Sie diese Zeile einfach am Anfang jeder Datei einfügen. Solange der Benutzer sich nicht explizit abgemeldet hat - ein Link zum Abmelden sollte natürlich immer vorhanden sein - ist keine weitere Eingabe des Passworts nötig.
Beachten Sie:
Die hier beschriebene Methode bringt natürlich auch gewisse Einschränkungen und Nachteile mit sich. Beispielsweise können im Gegensatz zur HTTP-Authentifizierung komplette Verzeichnisse nicht ohne weiteres geschützt werden, der Schutz beschränkt sich lediglich auf Dateien, die von PHP geparst werden – in der Regel also nur Textdokumente wie z.B. HTML- oder XML-Dateien.