1. Was ist eine Sitzung?
Je nach Definition ist eine Sitzung etwas, was über einen längeren Zeitraum andauert. In unserem Fall könnte man z.B. Denken es ist damit der Zeitraum zwischen Beginn und Ende der Internetbenutzung, die an einem Stück stattfindet. Wenn man jetzt vom Flatrate-Verhalten absieht könnte z.B. die Internetnutzung an der Arbeit um 9:00 Beginnen und um 12:00 enden. Diesen Zeitraum nennt man dann Sitzung.
Für Webserver (und somit für PHP) existiert der Begriff Sitzung nicht so wie wir ihn kennen, besonders nicht in dieser zeitlichen Länge. Für ihn ist eine Sitzung vom Verbindungsaufbau vom Client bis zum Senden der Antwort (meist HTML-Code), oder schlicht einfach die Dauer des Requests. Dabei ist jede Anfrage in sich geschlossen, es gibt (für den Webserver) keine semantische Verbindung zwischen zwei Anfragen. Dies kann unerwünscht sein, wenn man sich z.B. das beliebte Beispiel eines Warenkorbs eines Internetshops anguckt. Dort würde man eine Sitzung mit der Verweildauer im Internetshop gleichsetzen. Denn die Produkte die der Kunde ausgewählt hat müssen im Warenkorb hinterlegt werden und wenn er zur Kasse geht wird der Warenkorb ausgelesen und die Bestellung wird abgeschlossen. Was für den Benutzer wie eine lineare Führung durch den Bestellprozess ist ist für den Webserver nichts anderes als voneinander unabhängige Requests von beliebige Clients. Er weiß nicht das der Request vor 5 Sekunden semantisch zum aktuellen Request passt.
2. Sessions in PHP
Wenn wir Daten von einem Skript an ein anderes Skript schicken benutzen wir z.B. Links mit GET-Variablen oder Formulare mit hidden-Feldern. Dabei stoßen wir auf einige Grenzen denn wir sollten keine überlange URLs generieren die alle Daten enthalten die wir weitersenden wollen. Auch sollten wir keine Formulare mit riesigen hidden-Feldern. Und es könnte abenteuerlich werden wenn wir versuchen Arrays weiterzuleiten.
Aus all den Problemen mit der Datenweitergabe gibt es in PHP ein Sitzungs-System, kurz Sessions. Das heißt das man sich als Programmierer nicht mehr so viel Gedanken machen muss wie man Daten von einem Skript an das nächste Skript übergibt. Denn bei Sessions wird nur eine Identifikationsmarke weitergereicht.
3. Session-IDs
Da für ein Webserver jeder Request unabhängig von den anderen ist müssen wir in PHP irgendwie die richtige Session auswählen die wir (weiter)verwenden wollen. Daher brauchen wir eine Identifikation für die zu wählende Session. Spontan könnte man an folgende Beispiele denken.
-
Die UserID - Die UserID von unserem Loginsystem wäre eine Möglichkeit eine Session zu identifizieren. Jedoch ist nicht überall wo PHP installiert ist auch ein Loginsystem programmiert, daher ne schlechte Wahl.
-
Anhand der IP - So unverwechselbar eine IP-Adresse auch ist, sie ist nicht für die Identifizierung einer Session geeignet. Wenn man mit mehreren Browsern gleichzeitig surft oder die selbe Internetleitung von mehreren Benutzern benutzt wird, kriegt plötzlich der eine Benutzer die Daten aus der Session die eigentlich jemanden anders gehört.
-
Fortlaufende Nummer - Es geht in die richtige Richtung. Jedoch kann man dann die Identifikationen der anderen Sessions erraten, wenn man z.B. weiß das der Admin mit sensible Daten vorher auf der Seite war und man einfach die nächst kleinere Sessionnummer nimmt. Siehe auch Session hijacking.
-
Zufällige Nummer - Dies ist die Lösung die üblicherweise verwendet wird.
Immer wenn eine Session generiert wird wird auch eine zufällige Identifikation erstellt,
die sog. Session-ID. Mit dieser findet der Webserver die
richtige Session wieder. Da diese Session-ID reicht um die richtige Session wiederzufinden
reicht es auch nur diese Session-ID an das nächste Skript zu schicken. Wenn
das nächste Skript die Session-ID sieht kann es die vorherige Session anhand dieser
ID wieder laden. Wenn man also URLs der Form index.php?PHPSESSID=6ead67974b4a6794...
sieht... das ist die Session-ID.
4. Erzeugen bzw. laden einer Session
Um in PHP eine Session zu starten bzw. zu erzeugen wird die Funktion session_start verwendet. Die selbe Funktion wird auch verwendet um eine vorherige Session zu starten. Somit braucht man sich keine Gedanken machen ob man nun eine alte Session laden möchte oder ob man eine neue beginnen möchte da man so oder so nur eine Funktion verwendet. Wenn eine Session-ID übergeben wurde und unter dieser ID eine Session vorhanden ist so läd die Funktion session_start die Session wieder. Wenn keine Session mit der angegebenen Session-ID gefunden wurde oder überhaupt keine Session-ID angegeben ist wird eine neue Session generiert.
5. Session benutzen
Nachdem eine Session erzeugt bzw. geladen wurde legt PHP ein neues Superglobales Array
$_SESSION
an. In diesem Array können nun alle Daten gespeichert werden die
man möchte. Da es ein normales Array ist wird es auch wie ein normales Array benutzt.
<?php
session_start();
// nun haben wir $_SESSION
$_SESSION['Foo'] = 'Bar';
echo $_SESSION['Username'];
?>
Man kann mit dem Array alles machen, jedoch sollte man nicht das ganze Array
als solches mit unset($_SESSION);
löschen. Die Session wird
abgespeichert wenn das PHP-Skript zuende ist oder die Funktion
session_write_close aufgerufen wird, je nach dem was früher eintritt.
Dann wird das $_SESSION
-Array ausgelesen und die
Daten in eine Daten gespeichert die später anhand der Session-ID identifiziert
wird. Je nach Installation und Konfiguration von PHP wird dieses Datei z.B.
als /tmp/sess_5dae76576c5a7e5c4c2a54
gespeichert. Dort
liegt sie dann bis sie wieder durch einen session_start-Aufruf
geladen wird.
6. Übermitteln der Session-ID
Um im nächsten Skript die richtige Session zu laden müssen wir die
Session-ID übermitteln. Es gibt allgemein 3 Wege Daten an ein
PHP-Skript zu senden (unabhängig von Sessions): GET,
POST und COOKIE. Die
Session-ID kann über jeden dieser Wege gesendet werden.
Als GET-Variable wird sie in der Form PHPSESSID=...
übermittelt, als POST-Variable in der Form <input type="hidden" name="PHPSESSID" value="..." />
und als COOKIE der Form PHPSESSID=...
. In der Regel
muss man sich entscheiden wie man die Session-ID übermittelt.
In URLs könnte sie verwirren, Cookies können deaktiviert sein und
nicht überall ist ein Formular vorhanden. Die Funktion
session_start versucht ein Cookie zu senden, in der
die Session-ID steht. Daher muss diese Funktion relativ früh
aufgerufen werden da sie entsprechend den HTTP-Header ergänzt.
Es gibt ne Menge an Konfigurationen
bezüglich Sessions wie z.B. der Transport der Session-ID. Hier könnte
auch die Einstellung session.use_trans_sid
interessant sein.
Auf jedenfall wir die Session-ID in der Konstante SID
abgelegt
(durch session_start
). Die Konstante hat die Form
name=...
, wobei name
der Variablenname der
Session-ID ist (standardmäßig PHPSESSID
) und die
Punkte die Session-ID entsprechen. Diese Konstante kann somit
relativ einfach in URLs verwendet werden.
<?php
echo '<a href="index.php?section=foobar&'.htmlspecialchars(SID).'">Link</a>';
?>
Auch wenn die Konstante SID
durch PHP erstellt wird wird ihr
Inhalt teils durch Benutzereingaben bestimmt. Wenn die Session-ID den
Inhalt " onload="alert(...);
enthält hätten wir eine
XSS-Lücke. Daher jagen wir die Konstante durch
htmlspecialchars.
Falls beide Werte getrennt benötigt werden (also Variablenname und Session-ID)
kann man die Funktionen session_name und session_id
verwenden.
7. Dauer einer inaktiven Session
Die Lebensdauer einer inaktiven Session ist sehr kurz, je nach Konfiguration etwa 30 Minuten. Wenn sie innerhalb dieser Zeit nicht geladen wird wird sie von PHP gelöscht und ist weg. Dies erklärt auch einige Fehlermeldungen in anderen Webanwendung wenn es plötzlich heißt "Ihre Session ist abgelaufen, melden sie sich bitte neu an". Dann hat man sich zuviel Zeit gelassen und PHP hat aufgeräumt und alte Sessions gelöscht. Dies bedeutet insbesondere das man Sessions nicht verwendet um persistente Daten zu speichern. Also bei einem Newssystem sollte man die News nicht in einer Session speichern. Sessions dienen wirklich nur der temporären Übertragen von vielen Daten.
8. Sicherheit von Sessions
Die Inhalte von einer Session können in der Regel nur vom PHP-Skript bestimmt werden, nicht durch den Benutzer. Anhand der Session-ID kann der Benutzer also nicht irgendwie die Session auslesen oder sogar bearbeiten. Daher sind alle Daten in einer Session vertrauenswürdig, es sei denn sie sind natürlich durch Benutzereingaben gefüllt. So braucht man z.B. für ein Login nicht die UserID und das Password speichern, die UserID reicht völlig. Denn wenn die UserID vorhanden ist hat man (hoffentlich) vorher auch geprüft ob der Login gültig ist.
Der Schwachpunkt ist hingegen die Session-ID. Da die Session-ID reicht um die richtige Session zu laden ist sie ein beliebtes Ziel von Crackern sie auszulesen und mitzubenutzen. Ein normaler Admin loggt sich ein und ein Cracker hijacked die Session-ID und benutzt seinen Account mit. Hier muss man sich was einfallen lassen wie z.B. die IP-Adresse mitspeichern aber der Kreativität von Cracker sind keine Grenzen gesetzt.
Da Sessions im Dateisystem normale Dateien sind und in
/tmp/
abgelegt werden können sie von jedem PHP-Skript
geladen werden oder sogar durch einen einfachen Texteditor auf dem
Server. So kann ein Cracker den Inhalt der Session nach belieben verändern
bzw. durch ein PHP-Skript auf den Server. Aber wenn der Cracker schon so weit
im System ist hat man ganz andere Probleme als eine Session mit komische Daten...