Claudius L.: Weitergabe der Session-ID per URL funktioniert nicht richtig

Hallo,

ich habe mich in letzter Zeit etwas mit Sessions und einem Login-System beschäftigt, stehe nun aber vor einem – zumindest für mich – etwas seltsamen und unerklärlichen Problem.

Nach dem groben Vorbild des Artikels Sessionbasiertes Loginsystem habe ich eine Funktion zugriffskontrolle(), welche im Großen und Ganzen das gleiche tut wie die Datei „auth.php“ im verlinkten Artikel. Allerdings prüfe ich nicht nur, ob der User eingeloggt ist, sondern auch noch ein paar andere Daten (wie z.B. seit wann er eingeloggt ist), so dass unter Umständen auch ein eingeloggter User zum (erneuten) Login weitergeleitet wird. Da ich möchte, dass man nach dem erfolgreichen Login wieder dorthin zurück gelangt, wo man war, bevor die Zugriffskontrolle beschlossen hat, man müsse sich neu einloggen, habe ich folgenden Code (Ausschnitt aus der Funktion zugriffskontrolle()):

session_unset();  
session_destroy(); // Löschen der alten Session  
  
session_name("sessionid");  
session_start(); // Neue Session starten, um aktuell angezeigte Seite speichern zu können  
  
$_SESSION['zeigeseite'] = $aktuell_id; // Speichern der ID der aktuell aufgerufenen Kategorie, zu welcher später nach dem Login weitergeleitet werden soll  
  
header("Location: http://".$_SERVER['SERVER_NAME'].linkzuid("login").my_sid(false)); // Die Funktion linkzuid() dürfte nicht weiter relevant sein. my_sid(false) ersetzt einfach nur "/&".SID (wenn keine Cookies aktiviert sind) bzw. ist sonst leer  
exit;

Auf der Login-Seite, auf die weitergeleitet wird, wird zunächst wieder

session_name("sessionid");  
session_start();

ausgeführt und dann geprüft, ob die angegebenen Login-Daten korrekt sind. Wenn ja, wird $_SESSION['zeigeseite'] ausgelesen und auf die entsprechende Seite weitergeleitet.

Soweit die Theorie. Wenn Cookies aktiviert sind funktioniert das auch einwandfrei. Allerdings tritt trotzdem schon dort die erste für mich seltsame Sache auf: auch nachdem die alte Session gelöscht wurde (session_unset() und session_destroy()) ist die im Cookie gespeicherte Session-ID nach dem Start einer neuen Session die gleiche wie vorher. Hat das seine Richtigkeit so? Sollte nicht eine zufällige neue Session-ID vergeben werden?
Ich meine zwar, einmal etwas gelesen zu haben, dass dieses Verhalten erklären könnte, allerdings kann ich den entsprechenden Text jetzt nichtmehr finden … und irgendwie scheint es mir ein wenig unlogisch.

Der wirklich problematische Teil kommt allerdings erst, wenn Cookies nicht aktiviert sind. Dann habe ich nach der Weiterleitung zur Loginseite nämlich keinen Zugriff mehr auf die vorher gespeicherte Variable $_SESSION['zeigeseite']. Ich meine auch schon zu wissen, wie es dazu kommt, allerdings nicht, _warum_.
Und zwar ist es (laut der Adresszeile meines Browsers) so, dass bei der Weiterleitung zur Login-Seite noch die alte Session-ID angehängt wird, obwohl davor ja die alte Session zerstört und eine neue Session gestartet wird. Ich rufe darum die Login-Seite mit der alten Session-ID in der Adresszeile auf. Dort wird wieder eine Session gestartet, allerdings scheint PHP nun festzustellen, dass die aktuelle Session-ID nicht mehr gültig ist, denn _jetzt_ wird plötzlich eine neue Session gestartet. Darum ist $_SESSION['zeigeseite'] auch nichtmehr verfügbar. Dass eine neue Session gestartet und nicht die alte fortgesetzt wird, schließe ich einfach aus der neuen Session-ID in der Adresszeile nach dem erfolgreichen Login.

Naja, ich denke meine Frage dürfte offensichtlich sein (zumindest falls die Problembeschreibung verständlich ist): Warum funktioniert es nicht ohne Cookies?

Ich freue mich auf eure Antworten,

viele Grüße,

Claudius

  1. Ohje, da habe ich natürlich noch etwas vergessen zu erwähnen …

    header("Location: http://".$_SERVER['SERVER_NAME'].linkzuid("login").my_sid(false));

    Durch eine RewriteRule befindet sich die Session-ID beim Aufruf natürlich schon im Querystring, wo sie hingehört. Tatsächlich aufgerufen wird index.php?linkzuid("login")/&SID. (Falls Cookies deaktiviert sind.)

    Und nur der Vollständigkeit halber gleich noch eine Ergänzung: Getestet habe ich bisher mit PHP 5.2.1, Apache 2.2.3 unter Windows XP Home Edition SP2.

    Gruß,

    Claudius

  2. Hello,

    Du hast Dir ein umfangreiches Paket vorgenommen.
    Das "Login" sollte immer von einer übergeordneten Seite der Hierachie aus erfolgen, da dann sowohl ein "Login" auf Basis eines Cookies, als auch ein Login auf Basis einer HTACCESS-Methode möglich ist.

    Außerden sollte man fürs erste Sessions und "Login" nicht vermischen.
    Die Session dient ausschließlich der Wiedererkennung des Clients, unabhängig davon, welche Rechte er im System genießt. Das "Login" dient der Verifikation der Rechte. Es sollte als stets zeitnah sein daher und bei jedem Request über die Datenbasis neu überprüft werden.

    Die Session wird ja auch bei jedem Request überprüft, bedeutet aber nicht, dass der User von einem Request zum nächsten noch dieselben Rechte hat.

    [...] Allerdings prüfe ich nicht nur, ob der User eingeloggt ist, sondern auch noch ein paar andere Daten (wie z.B. seit wann er eingeloggt ist)

    Meinst Du, wann er sein "Zertifikat" das letzte mal verlängert hat oder wann er es sich angefordert hat? Eine Session wird unter PHP mit jedem Request (der die Sessiondatei verwendet) nachgetriggert.

    session_destroy(); // Löschen der alten Session

    Willst Du dem User tatsächlich seine mühevolle Vorarbeit löschen, nur weil er mal eine Minute zu lange  überlegt hat?

    Für intelligente Systeme solltest Du Session, "Login" und Vorgänge streng voneinander trennen. Mit einem unbeendeten Vorgang kann man den User solange wieder behelligen, bis er ihn beendet oder willentlich abgebrochen hat. Vorgänge sind sessionübergreifend, können aber selbstverständlich auch innerhalb einer Session begonnen und beendet werden.

    Login ist nur an den User gebunden, nicht an Vorgänge oder Sessions. Ausschließlich in der Person des Users liegt der Grund für Rechte oder keine Rechte. Mache niemals Userrechte von einer Session abhängig.

    Die Session ist nur (Transport-)Träger von RechteVERWALTUNG  und Vorgängen.

    Ohne Session wird es keine Rechteverwaltung geben und auch keine Vorgänge.
    Spätestens hier haben wir eine Differentialgleichung... :-)

    Mal Dir ein Bild für die Zusammenhänge, dann wir des klarer.

    Liebe Grüße

    Tom vom Berg

    --
    Nur selber lernen macht schlau
    1. Hallo Tom und danke dir für die ausführliche Antwort.

      Das "Login" sollte immer von einer übergeordneten Seite der Hierachie aus erfolgen, da dann sowohl ein "Login" auf Basis eines Cookies, als auch ein Login auf Basis einer HTACCESS-Methode möglich ist.

      Damit magst du generell Recht haben (auch wenn ich zugeben muss, nicht 100%-ig verstanden zu haben, wie du das genau meinst), allerdings ist ein Login auf HTACCESS-Basis in meinem Fall nicht nötig – behaupte ich einfach mal.

      Außerden sollte man fürs erste Sessions und "Login" nicht vermischen.
      Die Session dient ausschließlich der Wiedererkennung des Clients, unabhängig davon, welche Rechte er im System genießt. Das "Login" dient der Verifikation der Rechte. Es sollte als stets zeitnah sein daher und bei jedem Request über die Datenbasis neu überprüft werden.

      Die Session wird ja auch bei jedem Request überprüft, bedeutet aber nicht, dass der User von einem Request zum nächsten noch dieselben Rechte hat.

      Auch damit hast du ansich bestimmt Recht. Da es in meinem System aber eigentlich nur ein mögliches Recht gibt (darf die Seite sehen oder nicht) ist eine Überprüfung der genauen Rechte nicht nötig, da jeder User – sofern es sich bei dem Besucher denn um einen eingeloggten User handelt – immer die gleichen Rechte hat.

      [...] Allerdings prüfe ich nicht nur, ob der User eingeloggt ist, sondern auch noch ein paar andere Daten (wie z.B. seit wann er eingeloggt ist)

      Meinst Du, wann er sein "Zertifikat" das letzte mal verlängert hat oder wann er es sich angefordert hat? Eine Session wird unter PHP mit jedem Request (der die Sessiondatei verwendet) nachgetriggert.

      Ich meine damit, wann der User sich eingeloggt hat. Ich denke, es ist doch einfach sicherer, wenn nach einer bestimmten Zeit ein erneuter Login erforderlich ist, oder? (Wie ich das vor habe ohne den User zu quälen siehe weiter unten.)

      session_destroy(); // Löschen der alten Session

      Willst Du dem User tatsächlich seine mühevolle Vorarbeit löschen, nur weil er mal eine Minute zu lange  überlegt hat?

      Nein. Aber klar, das musst du denken, weil ich oben nur eine vereinfachte Version meines Codes gepostet und beschrieben habe. Ich dachte, das würde die Sache vereinfachen, aber es war wohl ein Fehler, da es zu Missverständnissen führt …

      Davon, dass die alte Session gelöscht wurde und es sich also um eine neue Session handelt, dürfte der User eigentlich nichts bemerken. Die Seite, die er aufzurufen im Begriff war wird wie ja schon erwähnt gespeichert und nachher wieder aufgerufen. Außerdem speichere ich nach dem erneuten Session-Start auch den Inhalt von $_POST in der neuen Session zwischen, so dass ich auch das nachher wieder herstellen kann. Es ist also so, dass ein User auch dann nichts von der neuen Session bemerken sollte, wenn er gerade ein Formular ausgefüllt hat, als die „Lebensdauer“ seiner Anmeldung ablief. Er wird dann einfach zum Login aufgefordert und danach werden seine eingegebenen Daten verarbeitet als sei nichts gewesen.

      Als ich das so gebastelt habe, habe ich mir gedacht, dass ich so an Sicherheit gewinnen könnte. Denn angenommen Cookies sind deaktiviert und der User gibt ungeschickterweise eine URL mit seiner Session-ID weiter, so wird dieser Link ja mit einer neuen Session „entschärft“. Würde ich die alte Session fortsetzen und nur eine erneute Authentifikation per Benutzername und Passwort anfordern, so würde die Session ja erst mit dem Logout des Users an Gültigkeit verlieren. – Oder?

      Deine weiteren Hinweise zum Verhältnis von Login und Session sind alle sehr interessant – aber ich glaube, so umfassend ist mein aktuelles Projekt nicht. ;-)
      Gerade was du zur Rechtevergabe sagst; wie gesagt gibt es bei mir quasi keinen Unterschied zwischen Login und Rechten, da jeder, der eingeloggt ist, das gleiche Recht hat.

      Aber ich werde – sollte ich mal etwas Größeres vorhaben – dein Posting noch(mehr)mals überdenken – vielen Dank also!

      Gruß,

      Claudius

      1. Hello,

        Auch damit hast du ansich bestimmt Recht. Da es in meinem System aber eigentlich nur ein mögliches Recht gibt (darf die Seite sehen oder nicht) ist eine Überprüfung der genauen Rechte nicht nötig, da jeder User – sofern es sich bei dem Besucher denn um einen eingeloggten User handelt – immer die gleichen Rechte hat.

        Das bedeutet also, dass der User die Rechte solange genießt, wie er es versteht, seine Session zu verlängern. Solltest Du aber zwischendurch mal einen User, der schon eine Session von drei Tagen Dauer unterhält und nun langsam lästig wird, loswerden wollen, wie machst Du das? Seine Session-ID kennt nur da System. Wie findest Du sie heraus?

        Liebe Grüße

        Tom vom Berg

        --
        Nur selber lernen macht schlau, Ignoranz hat noch nie geholfen
        1. Hallo Tom!

          Das bedeutet also, dass der User die Rechte solange genießt, wie er es versteht, seine Session zu verlängern.

          Neee.
          In der Session speichere ich die User-ID, dass der User eingeloggt ist und den Zeitstempel, wann der Login erfolgt ist. Bei der Zugriffskontrolle wird geprüft, ob der User eingeloggt ist und wie lange der Login schon her ist. Wenn der Login eine bestimmte Zeit zurückliegt, wird der User zur erneuten Eingabe von Benutzernamen und Passwort aufgefordert und danach (wie oben schon beschrieben) nahtlos dorthin weitergeleitet, wo er eigentlich gerade hin wollte (mitsamt seinen $_POST Daten, so dass auch Eingaben nicht verloren gehen).
          Bisher wird beim erneuten Login eine neue Session gestartet, was aber nach euren Aussagen ziemlich sinnfrei ist – richtig?

          Solltest Du aber zwischendurch mal einen User, der schon eine Session von drei Tagen Dauer unterhält und nun langsam lästig wird, loswerden wollen, wie machst Du das? Seine Session-ID kennt nur da System. Wie findest Du sie heraus?

          Dazu kommt es garnicht, da die Sessions eben in regelmäßigen Abständen gelöscht werden. Nämlich immer dann, wenn der User etwas aufruft und die Zugriffskontrolle feststellt, dass sein Login zu lange zurück liegt.

          Gruß,

          Claudius

  3. session_unset();
    session_destroy(); // Löschen der alten Session

    session_name("sessionid");
    session_start(); // Neue Session starten, um aktuell angezeigte Seite speichern zu können

    Das ist unnötig. Es reicht völlig, wenn du die Daten in $_SESSION löschst, und ich vermute, dass sich dann auch dein Problem löst.

    Die Kennung ändert sich prinzipbedingt immer erst von einem Aufruf zum nächsten, d.h. erst wenn sie vom Server zum Browser und dann wieder zum Server zurück kommt, kann man davon ausgehen, dass die Änderung auf allen Seiten vollzogen ist. Auch wenn ich auf den ersten Blick keinen Grund sehe, habe ich diese Eigenart in Verdacht, dein Problem auszulösen.

    Von einer neu ausgewürfelten Kennung hast du jedenfalls nichts, eine alte ohne bzw. mit gelöschten Daten ist genauso gut wie eine frische, neue.

    header("Location: http://".$_SERVER['SERVER_NAME'].linkzuid("login").my_sid(false));

    Durch eine RewriteRule befindet sich die Session-ID beim Aufruf natürlich schon im Querystring, wo sie hingehört.

    Wenn sie dahin gehört (was vollkommen richtig ist), warum wird sie dann erst vom Server dorthin befördert? Sich ändernde Parameter gehören immer in den, Überraschung, Parameterteil der URL, auch und gerade dann, wenn die URL von externen Stellen verarbeitet wird (hier: Browser).

    1. Hallo Gonzo!

      Das ist unnötig. Es reicht völlig, wenn du die Daten in $_SESSION löschst, und ich vermute, dass sich dann auch dein Problem löst.

      […] Von einer neu ausgewürfelten Kennung hast du jedenfalls nichts, eine alte ohne bzw. mit gelöschten Daten ist genauso gut wie eine frische, neue.

      Mhm. Wie gesagt hatte ich mir vorgestellt, dass das System sicherer wird, wenn sich die Session-ID in bestimmten Intervallen verändert. Das würde ja nach genau diesem Intervall weitergegebene Links mit SID entschärfen. Wenn das allerdings Quatsch oder unmöglich sein sollte, natürlich nicht …

      Die Kennung ändert sich prinzipbedingt immer erst von einem Aufruf zum nächsten, d.h. erst wenn sie vom Server zum Browser und dann wieder zum Server zurück kommt, kann man davon ausgehen, dass die Änderung auf allen Seiten vollzogen ist. Auch wenn ich auf den ersten Blick keinen Grund sehe, habe ich diese Eigenart in Verdacht, dein Problem auszulösen.

      Schon, hier geht es aber ja darum, dass selbst der Server nicht die neue ID zu verwenden schein – dass sie beim Klienten erst nach einem neuen Aufruf ankommt, ist mir klar.
      Aber schematisch passiert ja das bei mir (alles innerhalb eines Skripts und eines Aufrufs):

      -> Session-ID: ABC
      -> Löschen der Session
      -> Starten einer neuen Session
      -> Session-ID: _immernoch_ ABC

      Neuer Aufruf mit im vorigen Aufruf per URL übergebener Session-ID

      -> Session starten
      -> Session-ID: CBA (also jetzt erst anders)
      -> Variablen der vorigen Session natürlich auch nichtmehr verfügbar weil neue Session-ID

      Durch eine RewriteRule befindet sich die Session-ID beim Aufruf natürlich schon im Querystring, wo sie hingehört.

      Wenn sie dahin gehört (was vollkommen richtig ist), warum wird sie dann erst vom Server dorthin befördert? Sich ändernde Parameter gehören immer in den, Überraschung, Parameterteil der URL, auch und gerade dann, wenn die URL von externen Stellen verarbeitet wird (hier: Browser).

      Ja, da hast du Recht. Irgendwie habe ich das vor lauter Gedanken an Clean URIs aus den Augen verloren. Da muss ich mal schauen, ob und wie ich das umstrukturiere.

      Gruß,

      Claudius