IPC 2012 Spring

Session-Verwaltung optimieren

Wer auf seiner Seite einen Login hat, verwaltet die Autentifizierung der User üblicherweise per Cookies oder Sessions. Letzteres ist dabei praktikabler, da Cookies deaktiviert werden können und Sessions dann immernoch über GET-Parameter an der URL die Autentifizierung gewährleisten. Wenn aber Sessions genutzt werden, werden die Daten der Session auch auf dem Server gespeichert. Wer hier nicht aufpasst, kann sich schnell seinen Server vollfrummeln. Deshalb gibt es in diesem Beitrag einige Tipps zur effektiven Verwaltung von Sessions.

Session nur starten, wenn es nötig ist
Wenn es nicht nötig ist eine Session zu starten, sollte man es auch nicht tun. Das bedeutet, wenn jemand Hilfeseiten oder das Impressum ansieht und nicht gleichzeitig auf dieser Seite sein Benutzername angezeigt werden soll, braucht man auch keine Session starten. Denn jedes session_start() legt eine 0-Byte-Datei an, auch wenn man keine Session-Variable mit Inhalt füllt. Das können schnell sehr viele solcher Dateien sein – unnötiger Ballast fürs Dateisystem. Da jedoch der User meist irgendwo seinen Nutzernamen sehen soll, um ihm mitzuteilen, dass er noch eingeloggt ist, wird meist auf jeder Seite die Session per session_start() gestartet. Das ist also noch nicht ganz so tragisch …

Tragisch wird es erst, wenn…
In der php.ini kann per session.save_path das Verzeichnis gesetzt werden, in dem die Session-Dateien gespeichert werden sollen. Da dort ein absoluter Pfad angegeben werden muss, hat man völlige Freiheit, wo man das tut. Standardmäßig ist /tmp dort angegeben. Eine Session hat ja aber eine bestimmte Laufzeit (session.cookie_lifetime und session.gc_maxlifetime). Nach dieser Laufzeit soll die Session als abgelaufen betrachtet werden (Standard sind 24 Minuten).
Und jetzt kommt das Problematische: PHP ermittelt bei jeder Anfrage eine Wahrscheinlichkeit, jetzt eine Garbage Collection durchzuführen (einstellbar mit session.gc_probability und session.gc_divisor). Wenn die Garbage Collection durchgeführt wird, werden alle Session-Dateien, die älter als session.gc_maxlifetime sind, gelöscht. Doof nur, wenn PHP keine Rechte hat Dateien aus /tmp zu löschen. Denn dann läuft dieses Verzeichnis in aller Seelenruhe voll und wenn man Pech hat, merkt man es erst, wenn man mal eine aufwändige Operation mit vielen temporären Daten hat. Dann ist die Platte einfach voll und Feierabend. Mir ist das ganze mal mit einem OPTIMIZE TABLE passiert. Je nach Tabellengröße können dabei viele temporäre Dateien entstehen, die eventuell auch nach /tmp geschrieben werden. Aber wenn voll, dann voll – Tabelle gecrasht und musste repariert werden.

Also lieber ein eigenes Verzeichnis für die Sessions anlegen, wo sich PHP auch von den Rechten her austoben darf. Dieses sollte aber nicht öffentlich zugänglich sein und noch viel weniger darf ein DirectoryListing möglich sein, denn sonst können Sessions gehijacked werden.

Wahrscheinlichkeit der Garbage Collection
Je nach Besucheranzahl sollte die Wahrscheinlichkeit, dass die Garbage Collection durchgeführt wird, nicht zu hoch gewählt werden. Standardmäßig steht die auf 0,1% (session.gc_probability = 1 und session.gc_divisor = 1000). Wer täglich mehrere Tausend Zugriffe hat, darf das aber ruhig herabsetzen. session.gc_divisor auf 10000 ist ein guter wert für große Seiten. Oder man kümmert sich einfach selbst um die Garbage Collection, indem man in der Zeit, wenn die wenigsten Besucher kommen per Cronjob das Session-Verzeichnis durchläuft und das Alter der Dateien überprüft. Was zu alt ist, wird gelöscht (huch, das klingt jetzt ungewollt politisch – wirklich nicht beabsichtigt).

Also wer mit Sessions arbeitet, sollte diese Tipps beherzigen. Wenn ihr noch weitere Tipps habt, freue ich mich über eure Kommentare.

Schlagwörter: , ,

10 Kommentare bisher »

  1. GhostGambler sagt

    am 5. Februar 2009 @ 09:42

    if (!empty($_REQUEST[session_id()])) {
      define("SESSION_STARTED", true);
      session_start();
    }

    dann noch beim Login einmal manuell und man braucht sich um das Starten von Sessions praktisch gar nicht mehr kümmern.

    Lesenswert ist übrigens folgendes Paper:
    http://www.acros.si/papers/session_fixation.pdf

  2. Dirk sagt

    am 5. Februar 2009 @ 10:26

    Alternative: Sessions in der Datenbank verwalten. PHP lässt das recht einfach zu und beseitigt zudem noch eine Handvoll anderer Probleme.

  3. Jan sagt

    am 5. Februar 2009 @ 12:06

    @Dirk: Welche Probleme behebt denn die Speicherung der Sessions in der DB? Meist hat die DB doch eigentlich schon genug zu tun.

  4. Jan sagt

    am 5. Februar 2009 @ 12:35

    @GhostGambler: Dein Tipp ist echt super! Aber wieso $_REQUEST[session_id()]? Damit funktioniert es bei mir nicht, weil ja die ID nicht der Array-Index von $_COOKIE bzw. $_GET wird sondern 'PHPSESSID'.

    Oder hab ich was falsch verstanden / konfiguriert?

    Hab mir phpinfo() angesehen und da ist in $_COOKIE['PHPSESSID'] meine Session-ID, der von Dir besagte Eintrag existiert nicht.

    PS: Habe Deinen Fehler glaube gefunden: Du meinst session_name() statt session_id(), richtig?
    Wäre also folgender Code:

    if (!empty($_REQUEST[session_name()])) {
      session_start();
    }
  5. Jan sagt

    am 5. Februar 2009 @ 12:51

    Noch kurz ne andere Frage an Dich, Ghostgambler:
    Ich habe bisher die Session immer per
    session_destroy()
    zerstört. Dabei wird aber komischerweise $_COOKIE['PHPSESSID'] nicht gelöscht. Ist das normal? Habe es jetzt "manuell" gelöscht mit setcookie und einem Expire-Datum in der Vergagenheit.

    PS: Erledigt.

  6. GhostGambler sagt

    am 5. Februar 2009 @ 20:54

    Ja, session_name() natürlich…

    Das Cookie stirbt doch eh am Ende der Session, das musst doch nicht löschen.
    Deutlich wichtiger ist da zum Beispiel beim Login session_regenerate_id(true) auszuführen – siehe dem verlinkten PDF für eine Erklärung, ansonsten halt einfach machen.

    Cookies in der Datenbank benötigen keine Schreibrechte auf das File-System mehr. Man kommt leicht an die Session-Daten ran. Man kann die User-Id separat speichern und so Sessions von einem Benutzer kicken ohne alle Dateien "aufmachen" zu müssen (DEL FROM sessions WHERE uid=$id).
    Und zu guter letzt: Wenn der Hoster so unglaublich dummblöd ist und man seine eigenen Sessions nicht mehr löschen kann, funktioniert der Garbage-Collector auf der Datenbank wenigstens ohne Access-Warning… (ja derartige (bekannte) Hoster gibt es, einen Namen nenne ich jetzt mal freundlicherweise nicht…)

  7. Andreas Stephan sagt

    am 6. Februar 2009 @ 13:27

    Schöner Artikel. Danke.

    Ein paar weitere Anmerkungen/persönliche Erfahrungen:

    1. Sessiondaten im Filesystem ablegen funktioniert zuverlässig nur so lange, wie die Applikation auf lediglich einem Server läuft, ansonsten müssen die Session Daten in MySQL, Mysqlite, Memcache oder anderen Persistenzschicht gehalten werden (NFS o.ä. ist erfahrungsgemäß zu lahm/unzuverlässig dafür).

    2.Den Session Parameter per GET zu übergeben, falls der User keine Cookies unterstützt ist imo keine besonders gute Idee (wenn man es einfach nur generell anstellt), außer man sorgt dafür, dass man dieses Verhalten für Bots deaktiviert. Ansonsten werden URLs unendlich oft indiziert, da der Bot bei jedem Aufruf der Seite eine neue Session ID bekommt.

  8. Sören sagt

    am 8. Februar 2009 @ 21:17

    @Andreas Stephan

    Zusatz zu deinem 2. Punkt:

    Außerdem ist das SESSION-Hijacking viel einfach, da die SESSION-ID wesentlich präsenter ist. Außerdem kopieren unerfahrene Benutzer gerne mal Links mit SESSION-IDs in Foren, wodurch Zugriff auf deren Account erlangt werden kann, sofern die Session nicht über HTTP_REMOTE_ADR und HTTP_USER_AGENT zusätzlich geschützt wird.

  9. WasDuSuchst sagt

    am 15. Februar 2009 @ 21:17

    Session Daten in die DB legen ist ohne ein vernünftiges locking aber auch nicht so einfach möglich. Der default Session Handler von PHP vergibt ein exklusives Locking auf die Session Daten im Filesystem, wenn man sich seinen eigenen Session-DB-Handler schreibt sollte man dies auf jeden Fall berücksichtigen. Hier wird dies noch ein bisschen näher beschrieben…

    http://www.wasdusuchst.de/2007/10/27/race-conditions-mit-ajax-und-php-sessions/

  10. Marcel sagt

    am 5. März 2009 @ 11:23

    >>>Das bedeutet, wenn jemand Hilfeseiten oder das Impressum ansieht und nicht gleichzeitig auf dieser Seite sein Benutzername angezeigt werden soll, braucht man auch keine Session starten. Denn jedes session_start() legt eine 0-Byte-Datei an, auch wenn man keine Session-Variable mit Inhalt füllt. Das können schnell sehr viele solcher Dateien sein – unnötiger Ballast fürs Dateisystem. Da jedoch der User meist irgendwo seinen Nutzernamen sehen soll, um ihm mitzuteilen, dass er noch eingeloggt ist, wird meist auf jeder Seite die Session per session_start() gestartet.<<>>Speicherung im File System<<<
    Darauf hat man aber nur Einfluß wenn man ein eigenen (Root) Server hat oder hal Lokal das laufen läßt. Wer sich ein einfaches Hostingpaket mietet, kann dies eh nicht beeinflussen und die meisten Provider werden dem Ordner auch mit den nötigen Rechten versehen, damit der Sessionmüll auch wieder gelöscht wird.

    @andreas stephan
    Das mit der Session in der Url ist schon richtig, aber ganz darauf verzichten würde ich nicht bei allen Anwendungen (zB. Shops). Man muß halt schauen wie man die Sid anhängt (nicht PHP überlassen) und das ganze dann dem Login und Cookie Status nach auswählen.

    @sören
    Da sollte man halt vor dem Aufruf von sensiblen Daten schauen, ob sich was am User verändert hat und notfalls nochmal das Passwort vom User verlangen.

Komentar RSS · TrackBack URI

Hinterlasse einen Kommentar

Name: (erforderlich)

eMail: (erforderlich)

Website:

Kommentar: