PHP/Seiten mit PHP schützen

Aus Mikiwiki
< PHP
Version vom 7. Februar 2009, 14:58 Uhr von Michi (Diskussion | Beiträge) (→‎Andere Authentifizierungsmethoden)
(Unterschied) ← Nächstältere Version | Aktuelle Version (Unterschied) | Nächstjüngere Version → (Unterschied)
Zur Navigation springen Zur Suche springen

Ein Zugriffsschutz für Bereiche einer Website wird häufig benötigt. Interessant sind solche Lösungen, wenn die Daten für den Vergleich der Nutzernamen aus einer Datenbank kommen. Der anspruchsvollere Teil ist dabei allerdings die Authentifizierung. Zuerst einmal muss klar sein, wie die Authentifizierung eines Webservers abläuft. HTTP weist mit entsprechenden Befehlen auf geschützte Seiten hin.

Fordert ein Webbrowser eine geschützte Seite an, so antwortet der Webserver mit der Statusmeldung "403 - geschützter Bereich". Erst nach dem Empfang dieser Meldung sendet der Browser die ihm bekannten Benutzernamen und Passwörter und versucht eine Autorisierung zu erreichen. Ist auch dieser Versuch erfolglos oder waren keine Passwörter gespeichert, so öffnet der Browser das bekannte Dialogfenster und fordert zur Eingabe von Benutzernamen und Passwort auf. Der Webserver vergleicht die empfangenen Daten nun mit den Zugriffsrechten auf die Ressource und gibt diese im Erfolgsfall frei. Andernfalls lehnt er die Verbindung mit der Statusmeldung "401 - Keine Autorisierung" ab.

Wurde PHP als Modul in den Apache HTTP Server kompiliert, so kann die Authentifizierung direkt vom PHP-Skript aus gesteuert werden. Im Sinne kompatibler Anwendungen ist das aber nicht zu empfehlen.

Mit der CGI-Version von PHP (auch unter Linux) können Seiten nur über das Dateisystem geschützt werden. Benutzer, die so zugreifen möchten, müssen also ausdrücklich als Benutzer im Betriebssystem angelegt werden. Die Vergabe der Rechte erfolgt dabei wie unter Unix üblich. Der Grund liegt in der Arbeitsweise von CGI. Wird ein Verzeichnis oder eine Datei mit Hilfe von Einstellungen im Betriebssystem geschützt, so wird die Anforderung des Webbrowsers an das betreffende Skript zurückgewiesen. Der davon betroffene Prozess kann jedoch keine Header erzeugen oder andere HTTP-spezifische Aktionen veranlassen, sondern nur eine Fehlermeldung an die Standardausgabe stdout senden - damit kann der Browser nichts anfangen. Für ihn ist der Vorgang beendet - Zugriff verweigert. Der Webserver wiederum sieht die Anforderung und liest eine Antwort, den Inhalt beurteilt er dabei nicht - der Vorgang wurde für ihn richtig beendet. So lässt sich natürlich keine Authentifizierung aufbauen.

Authentifizierung mit Apache-Modul

Wurde PHP als Modul im Apache HTTP Server einkompiliert, so kann direkt auf die Header zugegriffen werden. Eingriffe durch die CGI-Schnittstelle stören dann nicht.

Das folgende Beispiel sendet dem Webbrowser einen Header mit dem HTTP-Fehler 401. Darauf fordert der Browser vom Nutzer die Eingabe eines Benutzernamens und Passworts. Diese werden mit der nächsten Anforderung übermittelt und stehen in PHP als Servervariable zur Verfügung. Die Variable "$_SERVER['PHP_AUTH_USER']" enthält den Benutzernamen, die Variable "$_SERVER['PHP_AUTH_PW']" enthält das Passwort im Klartext. Es ist nun Sache des Skripts, den Benutzernamen und das Passwort auszuwerten und beispielsweise mit den Einträgen in einer vorhandenen Datenbank zu vergleichen.

<?php
if(!isset($_SERVER['PHP_AUTH_USER'])) 
{
  header("WWW-Authenticate: Basic realm=\"My Realm\"");
  header("HTTP/1.0 401 Unauthorized");
  $msg = "Zugriff nicht gestattet\n";
  echo $msg;
}
else 
{
  $msg = <<<MESSAGE
  <p>Hallo {$_SERVER['PHP_AUTH_USER']}</p>
  <p>Sie wurden mit Passwort {$_SERVER['PHP_AUTH_PW']} angemeldet.</p>
MESSAGE;
}
?>
<h3>Seiten schützen: Authentifizierung</h3>
<?php
echo $msg; 
?>

Die Variable "realm" im Authentication-Header wird genutzt, um dem Webbrowser eine Zuordnung der Server zu geschützten Seiten zu ermöglichen. Gespeicherte Kombinationen aus Benutzernamen und Passwort werden immer einem sogenannten Realm (dt. Gebiet, Reich) zugeordnet - also dem Server. Soll aus Sicherheitsgründen innerhalb bestimmter Zeitabstände die erneute Eingabe des Benutzernamens erzwungen werden, so werden einfach die Parameter geändert. Der Browser kann dann die Zuordnung nicht mehr finden, sendet den alten Benutzernamen nicht und wird vom Skript zum Einblenden des Anmeldefensters gezwungen.

Wird eine externe Authentifizierung genutzt (z. B. über das Dateisystem in Linux), so werden die Servervariablen "$_SERVER['PHP_AUTH_USER']" und "$_SERVER['PHP_AUTH_PW']" nicht gesetzt. Sich anmeldende externe Nutzer werden dann in der Servervariable "$_SERVER['REMOTE_USER']" gespeichert. Die Verifizierung muss dann über das Benutzer- und Rechtesystem des Betriebssystems erfolgen.

Einen Ausweg für die Verwaltung einer grossen Nutzerzahl ohne die Abhängigkeit von einer bestimmten Webserverkonfiguration bietet eine Datenbanklösung auf der Basis von MySQL an.

Andere Authentifizierungsmethoden

Das Einblenden des Anmeldefensters ist sicher die eleganteste Methode zur Nutzerkontrolle. Es können aber auch Formulare verwendet, der Inhalt auf der folgenden Seite mit einer Datenbank abgeglichen und dann die weitere Skriptverarbeitung zugelassen oder abgelehnt werden. Das folgende Beispiel zeigt eine solche Vorgehensweise, wobei die registrierten Benutzer in einer Textdatei verwaltet werden.

<?php
$logok = FALSE;
if (isset($_POST['_login']) and isset($_POST['_pass'])) 
{
  $_login = $_POST['_login'];
  $_pass  = $_POST['_pass'];
  $fp     = fopen("data/passwd.txt", "r");
  while ($line = fgets($fp, 256)) 
  {
    $arr = explode(",", $line);
    if ((trim($arr[0]) == trim($_login)) and (trim($arr[1]) == trim($_pass))) 
    {
      $logok = TRUE;
      break;
    }
  }
}
if (!$logok) 
{
  echo <<<FORMDATA
<p>Bitte geben Sie Benutzernamen und Passwort ein:</p>
<form method="post" action="{$_SERVER['PHP_SELF']}">
  <table border="0">
    <tr>
      < td>Name:</td>
      <td><input type="text" size="30" name="_login"></td>
    </tr>
    <tr>
      <td>Kennwort:</td>
      <td><input type="text" size="30" name="_pass"></td>
    </tr>
  </table>
  <input type="Submit" name="submit" value="Anmelden">
</form>
<p><span style="color:red; font-weight:bold">
  Dieser Text erscheint nur, wenn die Authentifizierung nicht erfolgreich war.</span>
</p>
FORMDATA;
} 
else 
{
  echo <<<SUCCESS
<p><span style="color:green; font-weight:bold">
  Dieser Text erscheint nur, wenn die Authentifizierung erfolgreich war.
  </span>
</p>
<p><a href="{$_SERVER['PHP_SELF']}">Zurück zum Formular</a></p>
SUCCESS;
}
?>

<html>

Bitte geben Sie Namen und Kennwort ein:

<form method="post" action="">

Name: <input type="text" size="30" name="">
Kennwort:<input type="text" size="30" name="">
 <input type="Submit" name="" value="Anmelden">
</form>

Dieser Text erscheint nur, wenn die Authentifizierung nicht erfolgreich war.

</html>

Die benötigte Textdatei "passwd.txt" hat einen einfachen Aufbau in der Form "Benutzername,Passwort".

mueller,abc123
meier,dtZ55r
schmid,tr9lp9

Mit dieser Lösung können beliebig viele Benutzernamen-/Passwort-Kombinationen überwacht werden. Sind erhöhte Sicherheitsansprüche vorhanden, so könnten die Einträge beispielsweise mit der Funktion "md5" verschlüsselt und bei der Prüfung entsprechend verfahren werden. Ein solcher Schutz ist jeden falls durch normale Nutzer der Website kaum zu durchbrechen. Auch ein Diebstahl der Passwortdatei wäre wertlos, da der MD5-Schlüssel nicht umkehrbar ist.