Sonntag, 7. September 2008

entwickler.com Magazine Konferenzen Entwickler Akademie Entwickler-Forum Jobbörse Bücher
Software & Support Verlag





April 2006
aus PHP Magazin Ausgabe: 04.2002
Mit PHP in Sitzung
Session-basierte Entwicklung mit PHP
von Arne Blankerts

Betrachtet man das WWW einmal aus der Sicht eines Webservers, so ist das Leben wohl ziemlich eintönig: Jeder Dateiabruf stellt ein für sich einzigartiges und zusammenhangloses Ereignis dar. Diesen vermeintlich traurigen Umstand verdankt der Kollege Webserver den Erfindern des Protokolls HTTP, welches - anders als die meisten anderen im Internet gängigen Protokolle - verbindungslos arbeitet und somit jede Anfrage isoliert betrachtet.


Dies mag bei einfachen, zumeist statischen Seiten noch akzeptabel - gar von Vorteil - sein, spätestens wenn benutzerabhängige Daten wie Einstellungen oder Warenkörbe in die Seite integriert werden, braucht die im Hintergrund werkelnde Anwendung jedoch fast immer mehr Informationen: Hat der Besucher bereits Waren ausgewählt? Ist der Benutzer angemeldet?
Um nicht ständig alle relevanten Daten händisch von Seite zu Seite mitschleifen zu müssen, könnte man Cookies verwenden. Nun haben diese beim Surfer abgelegten Kekse jedoch einen gravierenden Nachteil: Sie liegen beim Benutzer und entziehen sich damit der Kontrolle der Anwendung. Wenn man auf die Richtigkeit der im Cookie gespeicherten Informationen angewiesen ist, stellt dies ein nicht zu unterschätzendes (Sicherheits-)Problem dar. Zudem kann der Benutzer die Annahme von Cookies verweigern - was dann?
Eine Session wird auf dem Server selbst verwaltet und nur die zur Verwaltung notwendige ID wird nach außen preisgegeben. Die hierbei einzig verbleibende Gefahr stellt das Entführen einer Session-ID dar. Wie sich dieses zumindest in Ansätzen verhindern lässt, klären wir später im Praxis-Teil (den notwendigen Quellcode finden Sie auf der Heft-CD sowie in Auszügen auf den nächsten Seiten).
Seit der Version 4 sind Session-Funktionen fester Bestandteil des Sprachumfangs von PHP. Wer auf die Verwendung älterer Versionen von PHP angewiesen ist, kann die Session-Funktionen der PHPLib (phplib.sourceforge.net/) zur Hilfe nehmen. Neben der für ältere Versionen notwendigen eigenen Implementation bietet die PHPLib zudem Unterstützung für das Session-System der PHP Version 4 (www.sanisoft.com/phplib/manual/php4_sessions.php).
PHP 4 und Apache stellen dem geneigten Entwickler eine Fülle an Möglichkeiten zur Übergabe einer Session-ID, kurz SID, zur Verfügung. Die zwei wohl gängigsten Methoden sind die Speicherung in einem Cookie und die Übergabe als GET- bzw. POST-Parameter (welche von PHP automatisch als Fallback bei ausgeschalteten Cookies verwendet wird). Weitere bekannte Möglichkeiten sind die Verwendung von Wildcard-DNS zur Speicherung der SID im Hostnamen [Wildcard] oder der Einsatz des Apache-Modules mod_rewrite.
Eine Sitzung wird in der Regel mit dem Aufruf der ersten Seite gestartet. Dies kann sowohl automatisch ( Einstellung session.autostart=1 in der php.ini) als auch durch den manuellen Aufruf von session_start() erfolgen. PHP durchsucht daraufhin automatisch die COOKIE-, GET- und POST-Variablen nach dem Vorhandensein einer gültigen SID.
Falls Sie eine von PHP abweichende Meinung über das Thema Gültigkeit einer SID haben oder Sie eine selbst entwickelte Methode zur Übergabe der SID verwenden, so kann die SID auch händisch durch den Aufruf von session_id($MeineID) vor dem Starten der Sitzung gesetzt oder die Verarbeitung abgebrochen werden.
Hat man sich bzw. PHP davon überzeugt, dass es sich um eine gültige Session handelt, stehen die in der Session abgelegten Daten zur Verfügung: Zum einen über das mit PHP 4.1.0 eingeführte superglobale Array $_SESSION, zum anderen über das inzwischen veraltete System-Array $HTTP_SESSION_VARS. Sollte Ihre PHP-Konfiguration aus Kompatiblitätsgründen zudem noch mit register_globals=ON arbeiten, so werden alle in $_SESSION respektive $HTTP_SESSION_VARS gespeicherten Daten in den globalen Variablenscope importiert und stehen somit als normale Variablen zur Verfügung.
Doch bevor man Daten aus einer Session erhalten kann, müssen erst einmal auch Daten abgelegt werden. Die optimale Methode zum Ablegen von Daten in einer Session hängt stark von der verwendeten PHP-Version und dem Schalter für register_globals ab.

PHP < 4.1.0 oder aktiviertes register_globals
Um dem Session-System mitzuteilen, welche Variablen in einer Sitzung zur späteren Wiederverwendung erhalten bleiben sollen, müssen diese registriert werden. Durch den einfachen Aufruf der Funktion session_register ist diese Aufgabe schnell erledigt. session_register() erwartet als Parameter einen String: Den Namen der zu registrierenden Variablen. Dies mag auf den ersten Blick nichts besonderes sein, birgt aber eine gewisse Gefahr: So ist für session_register() die Zeichenkette data['key'] nicht das vermeintlich beabsichtigte Element key aus dem data-Array sondern eben nur der Name der Variablen; die von PHP verwendeten Klammern zur Kennzeichnung eines Arrays werden nicht interpretiert. Da es also die Variable mit dem Namen data['key'] nicht gibt, wird das Ergebnis in der Session leider ein leerer Wert sein. Ist die Variable jedoch einmal registriert, wird jede Änderung im Wert von PHP automatisch erkannt und am Ende der Skriptverarbeitung gespeichert.

... und ohne register_globals?
Auch wer auf die Verwendung der automatischen Registrierung der Variablen im globalen Variablenscope verzichtet, kann natürlich mit Sessions arbeiten. Vor der Version 4.1 gibt es hierzu das System-Array $HTTP_SESSION_VARS. Das Array steht nach dem Start einer Session zur Verfügung und kann wie jedes Array angesprochen werden. Eine Registrierung mit session_register() ist nicht notwendig und sollte sogar vermieden werden, da es sonst beim Einsatz auf Systemen, die register_globals weiterhin aktiviert haben, zu Inkonsistenzen führen kann. Man sollte sich also für eine Methode entscheiden und diese dann auch durchgängig beibehalten.

PHP ab Version 4.1 (mit ausgeschaltetem register_globals)
Nach dem Start einer Session ist das System-Array $_SESSION definiert und kann wie gewohnt bearbeitet werden. Im Gegensatz zu normalen Arrays und sonstigen Variablen ist das $_SESSION-Array superglobal und damit ohne vorgestelltes global in Funktionen und Klassen verfügbar.
In einer Session-Variablen lässt sich so ziemlich alles ablegen, was man auch in PHP selbst in Variablen speichern kann. Hierbei gilt es zu beachten, dass das Speichern von Link-IDs zu Dateien oder Datenbankverbindungen zwar natürlich technisch möglich, jedoch nur sehr bedingt sinnvoll ist: Schließlich werden die meisten Verbindungen beim Beenden des Skripts automatisch geschlossen und wären bei einem späteren Zugriffsversuch somit ungültig. Das Ablegen einer Instanz einer Klasse hingegen stellt kein Problem dar - wichtig ist hier lediglich, dass die Session erst nach der Definition der Klasse gestartet wird.
Um einmal in die Session aufgenommene Daten wieder los zu werden, reicht ein einfaches unset() des entsprechenden Elements des Arrays $_SESSION / $HTTP_SESSION_VARS oder - falls die Variable via session_register() angemeldet wurde - der Aufruf von session_unregister().

Nacharbeiten ...
Da der Mensch - und auch Entwickler gelten als solche - gerne vergesslich und zudem auch faul ist, eine Session ohne gültige SID aber nicht funktioniert und es überhaupt bekanntlich sicherer ist, wenn man sich selbst um alles kümmert, wurde in PHP eine automatisierte Methode zur SID-Übergabe eingebaut. Ist PHP mit der Option --enable-trans-sid kompiliert worden (erst ab Version 4.2 ist dies default), so kümmert sich das System sowohl für den Entwickler als auch den Surfer vollkommen transparent darum, dass eine einmal gestartete Sitzung nicht unvermittelt abreißt. Dies bedeutet vor allem bei deaktivierten Cookies für PHP einiges an Arbeit: An alle Links wird die SID als GET-Parameter, bei Formularen als verstecktes Feld, eingefügt. Einzig der universell verwendbare Header()-Befehl wird von diesem Automatismus nicht erfasst - hier obliegt es dem Entwickler die SID im Bedarfsfall selbst anzuhängen.

Endlich Praxis
Kommen wir nach der grauen Theorie zu einem Anwendungsbeispiel mitten aus dem Leben: Ein Session-basiertes Login.
Die im HTTP implementierte Basic-Auth-Methode (durch versenden des Header-Codes 401, www.php.net/manual/de/features.http-auth.php) ist verhältnismäßig unsicher, da mit ihr abgefragte Logindaten im System des Surfers zwischengespeichert werden und somit nur mit dem Beenden des Browers ein wirksames Abmelden zu realisieren ist. Hier spielt ein Session-System eine seiner Stärken aus: Durch einfaches Löschen bzw. Beenden der Session ist der Surfer auch schon abgemeldet. Da eine Session in ihrer Gültigkeit zudem zeitlich begrenzt ist, verfällt sie nach einem vorgegebenen Zeitrahmen ohne Nutzung automatisch.
Bevor der Anwender nun unseren geschützten Bereich betreten darf, muss er sich erst einmal Anmelden. Hierfür verwenden wir das in start.php (Sie finden alle Listings auf der Heft-CD.) enthaltene einfache HTML-Formular. Das in der Action festgelegte Skript in Listing 1 [login.php] ist für die Überprüfung der Login-Daten und im Erfolgsfall für den Start einer neuen Session zuständig. Der Benutzer wird automatisch entweder in den geschützten Bereich oder zurück zur Login-Seite geschickt.

Listing 1

<?php

// login.php

// Keine Cookies
ini_set("session.use_cookies", "0");

// Überprüfung der Benutzerdaten
if ($_POST["user"]=="demo" && $_POST["passwd"]=="demo") {
// login ok - session starten

session_name('PHPMAG');
session_start();

// Usernamen speichern
$_SESSION["user"]=$_POST["user"];

// Session verifiziert
$_SESSION["VERIFIED"]=true;

// Benutzer-IP speichern
$_SESSION["REMOTE_ADDR"] = $_SERVER["REMOTE_ADDR"];

Header('Location: /inside.php?PHPMAG='.session_id());
} else {
// login nicht ok

Header('Location: /start.php?fail=true');
}


?>

Um nicht in jeder Seite den gleiche Code schreiben zu müssen, lagern wir den Code zur Überprüfung der Session und der in ihr gespeicherten Daten in ein include aus (session.inc.php, siehe CD). Um sicherzustellen, dass die übermittelte und von PHP bereits als gültig eingestufte SID auch vom gleichen Benutzer ist, überprüfen wir den Referer, die URL der vorherigen Seite sowie die IP des Benutzers. Stimmt die IP nicht mit der in der Session gespeicherten überein oder ist im Referer nichts von unserer Seite zu finden, so kann man mit großer Sicherheit davon ausgehen, dass hier eine SID entführt wurde. Als Folge dieser Erkenntnis erklären wir die Session als ungültig und stellen die Verarbeitung ein.
Die mit inside.php angebotene Seite ist im geschützten Bereich und begrüßt den Anwender namentlich. Neben der Option sich gleich wieder abzumelden, wäre hier jetzt der richtige Ort, um zu eigenen Anwendungen und Funktionen zu verzweigen.
Das mit der Option zum Abmelden verlinkte Skript (siehe Listing 2 [logout.php]) löscht alle Daten in der Session, beendet selbige und schickt den Surfer automatisch zur Anmeldeseite. Der Benutzer ist erfolgreich abgemeldet und auch über ein Zurückblättern in der History des Browsers lassen sich die vorherigen Seiten nicht mehr abrufen.

Listing 2

<?php

session_name("PHPMAG");
session_start();
session_destroy();

Header("Location: start.php");

?>

Die hier gezeigten Beispiel-Listings setzen PHP ab der Version 4.1 voraus und sind auf der Heft-CD zu finden. Um die Beispiele mit früheren PHP-Versionen der Generation 4 zu verwenden, bedarf es nur einer kleinen Änderung: Tauschen Sie alle Vorkommen von $_SESSION,$_POST und $_GET durch die entsprechende $HTTP_*_VARS-Version aus und deaktivieren sie die register_globals-Funktion.


    Hat Ihnen dieser Artikel gefallen? Dann abonnieren Sie das PHP Magazin direkt über unser

zur vorherigen Seite
zurück
an den Anfang der Seite
nach oben
Diesen Artikel drucken
drucken
Diesen Artikel weiterempfehlen
empfehlen

Software & Support Verlag GmbH