Mike: Session wenn kein Cookie, dann auch keine Session

Hi,

wahrscheinlich Brett vorm Kopf....

  
### Session ####  
  
// Hier soll das komplette Cookie Array gelöscht werden und print_r bestätigt mir das auch. $_GET['ses']=='del' ist natürlich vorhanden.  
  
if($_GET['ses']=='del'){unset($_COOKIE);print_r($_COOKIE);echo'Cookie sollte nicht mehr existieren';}  
  
  
  
// Also dürfte doch das hier gar nicht mehr passieren  
if($_COOKIE){session_start();echo'Cookie existiert';}

Warum wird session_start() trotzdem ausgeführt?

Mike

  1. Hi,

    Warum wird session_start() trotzdem ausgeführt?

    den Grund weiss ich zwar immer noch nicht aber bei  Lösungsmöglichkeiten kam eine neue Frage. Ich wollte den clientseitigen Cookie löschen, gemäss Manual. Das ging aber nicht, zumindest so nicht:

    setcookie(session_name(), "", time() - 3600);

    Wenn ich allerdings den 4. parameter verwende, dann schon:

    setcookie(session_name(), "", time() - 3600,'/');

    Warum muss das so sein, denn es sollte doch auch ohne diesen Parameter funktionieren?

    Ich habe da mal eine Vermutung. Kann es sein, weil ja dieser Cookie durch die Sessionprozedur von PHP gesetzt wurde, eben genau mit diesem Parameter gesetzt wurde und auch deshalb nur damit zu entfernen ist?

    Wenn dem so ist, ist das ganz schön doof. Denn die Sessionfunktionen setzen zwar einen Cookie beim User, aber eine Funktion zum Enfernen haben sie nicht, richtig?

    Also muss das manuell gemacht werden, nur woher weiss ich denn auf welchem System unter welcher Konfiguration der Cookie wie gesetzt wurde mit 4. Parameter, mit oder ohne Pfad, usw... Wie kann ich da immer sicher sein, das meine Löschaufruf auch wirklich auf jedem System eine Löschung bewirkt?

    Schön wäre also wenn session_destroy() das direkt mit übernehmen würde, aber so was gibt es nicht, oder?

    Mike

    1. Hi,

      und noch eine Frage.

      Ich mache Cookies zur Pflicht durch "ini_set('session.use_only_cookies',1);"

      Zumindest dachte ich so, aber wenn ich Cookies im Browser deaktiviere, wird zwar die Sessionprozedur nicht verarbeitet, aber das TMP-Verzeichnis füllt sich trotzdem und zwar sogar mit jeweils einer Datei je Seitenaufruf.

      Ist das ein Bug?

      Mike

      1. Hello,

        und noch eine Frage.

        Ich mache Cookies zur Pflicht durch "ini_set('session.use_only_cookies',1);"

        Zumindest dachte ich so, aber wenn ich Cookies im Browser deaktiviere, wird zwar die Sessionprozedur nicht verarbeitet, aber das TMP-Verzeichnis füllt sich trotzdem und zwar sogar mit jeweils einer Datei je Seitenaufruf.

        Ist das ein Bug?

        Ja, aber in Deinem Kopf ;-P

        Wenn Du 'session.use_only_cookies' auf true setzt, dann weist Du PHP damit an, nur Cookies zu benutzen für den Sessionkey-Transport und keinen in der URL sichtbaren Key und auch keine <hidden>-Elemente in eventuell vorhandenen Formularen.

        Wenn nun ein session_start() aufgerufen wird, schaut PHP zuerst in den Cookies, ob dort ein Session-Cookie (der Name muss gleich dem Ergebnis von session_name() sein) vorhanden ist. Ist ein passender da, wird nach einer bestehenden Session mit dem übertragenen Wert gesucht. Wird diese gefunden, wird sie fortgesetzt. Wird keine passende Session gefunden, z.B. weil gar kein Cookie übertragen wurde, dann wird eine neue Session gestartet, dafür eine neue Sessiondatei angelegt und ein neuer, dazu passender Cookie an den Client mitgeschickt. Kommt der dann beim nächsten Request wieder mit zum Server, kann die Session fortgesetzt werden.

        Wenn der Client keine Cookies akzeptiert und Du 'session.use_only_cookies' auf true gesetzt hast, kannst Du mit dem automatischen Session-Support von PHP keine Sessions führen.

        Weitere Möglichkeiten, eine Client-Erkennung durchzuführen, wäre nun noch Basic-Authentification. Dann erscheint am Client aber das "hässliche Anmeldefenster". Außerdem gibt es bei basic auth keine Möglichkeit, sich "abzumelden", außer alle Browserfenster zu schließen.

        Liebe Grüße aus dem Cyberspace

        Tom vom Berg

        --
        Nur selber lernen macht schlau
        http://bergpost.annerschbarrich.de
    2. Hi Mike,

      unset($_COOKIE);

      $_COOKIE ist eine superglobale Variable, solche solltest du nie per unset() gänzlich löschen, da diese dann für alle Teile deines Scriptes nicht mehr zur Verfügung steht, was zu unerwarteten Problemen führen kann.

      Aus diesem Grund solltest du, wenn du alle Daten aus $_COOKIE, $_SESSION etc. löschen willst nie unset() verwenden, sondern die Variable als leeres Array definieren:

      $_COOKIES = array()

      Beachte aber bitte, dass du hierdurch nur die Variablen in PHP löschst, welche für die vom Browser gesendeten Cookies stehen, keinesfalls aber die Cookies selber löschst. Den Browser anzuweisen ein Cookie zu löschen kannst du auscchließlich mit setcookie().

      Warum wird session_start() trotzdem ausgeführt?

      Das kann ich dir leider auch nicht sagen, ich habe versucht dein Problem an folgendem Code nachzuvollziehen:

      ~~~php var_dump($_COOKIE);
        unset($_COOKIE);
        var_dump($_COOKIE);
        if ($_COOKIE) {
          echo 'wahr';
        } else {
          echo 'falsch';
        }

        
      Mit PHP 5.2.6 erhalte ich da folgende Ausgabe:  
        
        array(0) {  
        }  
        Notice: Undefined variable: \_COOKIE in /home/driehle/test.php on line 3  
        NULL  
        Notice: Undefined variable: \_COOKIE in /home/driehle/test.php on line 4  
        falsch  
        
      Alles so, wie ich es erwartet hätte, dein Problem kann ich also nicht nachvollziehen. Vermutlich ist dein Original-Code länger und du hast vielleicht irgendeine wichtige Stelle fürs Posting hier herausgestrichen?  
        
        
      
      > Ich habe da mal eine Vermutung. Kann es sein, weil ja dieser Cookie durch die Sessionprozedur von PHP gesetzt wurde, eben genau mit diesem Parameter gesetzt wurde und auch deshalb nur damit zu entfernen ist?  
        
      Richtig, genau so ist es. Mit welchen Optionen session\_start() das Session-Cookie setzt kannst du aber über einige INI-Optionen beeinflussen. In deinem Fall kommt [session.cookie_path](http://de.php.net/manual/en/session.configuration.php#ini.session.cookie-path) zum Tragen, an der verlinkten Stelle im Handbuch steht auch, dass der Wert '/' der Default ist. (Was notwendig ist, weil dir das Session-Cookie sonst nicht für alle PHP-Scripte unter der verwendeten Domain zur Verfügung stände.)  
        
      Wenn du das Session-Cookie bei jeder möglichen PHP-Konfiguration zuverlässig löschen willst, könntest du folgendes probieren:  
        
        ~~~php
      setcookie(  
           session_name(),  
           '',  
           time() - 3600,  
           ini_get('session.cookie_path'),  
           ini_get('session.cookie_domain'),  
           ini_get('session.cookie_secure'),  
           ini_get('session.cookie_httponly')  
         );
      

      Alternativ kannst du vor dem Starten der Session natürlich auch die Optionen für das Session-Cookie per ini_set() oder per session_set_cookie_params() explizit setzen, dann brauchst du sie beim Löschen des Cookies nicht mehr aus der PHP-Konfiguration abzurufen.

      Wenn dem so ist, ist das ganz schön doof. Denn die Sessionfunktionen setzen zwar einen Cookie beim User, aber eine Funktion zum Enfernen haben sie nicht, richtig?

      Anders gefragt: Warum willst du das Session-Cookie überhaupt löschen? Standardmäßig hat ein Session-Cookie eine Lifetime von 0, was bedeutet, dass der Browser das Cookie löscht, sobald er beendet wird. Startet der Besucher also seinen Browser neu, ist das Cookie eh weg.

      Wenn du möchtest, dass der User seine Rechte verliert, welche er eventuell besitzt, weil er sich gerade eingeloggt hat, so ist der gängige Weg, dass du nicht das Cookie beim Client löschst, sondern per

      $_SESSION = array();

      die Daten bei dir auf dem Server. Dann kann kommen was oder wer(!) will, die Session-ID „gibt es zwar noch” (und zwar in der Form, dass der Server noch eine entsprechende Datei irgendwo abgelegt hat), die Session selber, also die Daten der Session gibts aber nicht mehr. Und somit ist diese nun leere Session gleichbedeutend mit einer nicht existenten Session.

      Schön wäre also wenn session_destroy() das direkt mit übernehmen würde, aber so was gibt es nicht, oder?

      session_destroy() würde ich gar nicht benutzen, weil es nicht viel bringt. Wie du die Daten einer Session gelöscht kriegst habe ich dir oben schon gezeigt. Was macht session_destroy() mehr? Es löscht noch die Session-Datei auf dem Server (das was du in deinem tmp Ordner siehst), aber das bringt dir nichts, weil du dich nicht darauf verlassen kannst, dass der Besucher sich auch wirklich ausloggt, sprich du hast keine Garantie, dass session_destroy() auch wirklich für jeder erzeugte Session am Ende auch wieder aufgerufen wird. Wenn der Besucher also einfach seinen Browser zu macht hast du in jedem Fall so oder so eine verwaiste Session-Datei auf dem Server.

      Was ich sagen will: Alte (verwaiste) Session-Dateien zu löschen ist aus meiner Sicht nicht deine Aufgabe als PHP-Programmierer, sondern eine Aufgabe von PHP mit seinem Session Garbage Collector oder Aufgabe des Betriebssystem durch z.B. einen Cronjob (so macht es Debian).

      Zu deinem hier beschriebenen Problem hat dir Tom ja bereits geschrieben - in deinem PHP-Script löschst du stets zuerst alle Cookies, folglich auch das Session-Cookie, da dieses auch nichts anderes als ein normales Cookie ist.

      Infolge des fehlenden Session-Cookies startet PHP nunmal bei jedem Seitenaufruf eine neue PHP-Session und folglich kriegst du für jeden Seitenaufruf eine neue Session-Datei in deinem tmp Ordner (diesen kannst du übrigens über session.save_path festlegen).

      Irgendwann werden diese Session-Dateien im tmp Ordner auch wieder gelöscht, allerdings frühestens, nachdem sie ihre maximale Lebenszeit erreicht haben. Das sind in der Standardeinstellung 1440 Sekunden, also 24 Minuten - dann erst könnten die Session-Dateien gelöscht werden.

      Aber: Das Aufräumen des tmp Ordners ist eine relativ aufwendige Aktione, da PHP hierfür alle Dateien angucken muss, schauen muss wann diese erstellt wurden, ob dies bereits länger als 24 Minuten her ist und wenn ja, diese dann zu löschen. Aus diesem Grund führt räumt PHP nicht bei jedem Seitenaufruf den tmp Ordner auf, sondern nur bei ca. 1% aller Seitenaufrufe. Das lässt sich über session.gc_probability und session.gc_divisor regeln. In der Standardkonfiguration löst also im Mittel nur jeder 100. Seitenaufruf eine Aufräumen des tmp Ordners aus, und auch dann werden natürlich nur die Dateien gelöscht, welche älter als session.gc_maxlifetime Sekunden alt sind.

      Insofern: Dass sich im tmp Ordner erst mal Dateien ansammeln ist durchaus normal.

      Viele Grüße,
        ~ Dennis.

      1. Hello,

        unset($_COOKIE);

        $_COOKIE ist eine superglobale Variable, solche solltest du nie per unset() gänzlich löschen, da diese dann für alle Teile deines Scriptes nicht mehr zur Verfügung steht, was zu unerwarteten Problemen führen kann.

        Aus diesem Grund solltest du, wenn du alle Daten aus $_COOKIE, $_SESSION etc. löschen willst nie unset() verwenden, sondern die Variable als leeres Array definieren:

        $_COOKIES = array()

        Nur Singular!

        $_COOKIE = array()

        sonst wird ein zweites Array angelegt :-D

        Liebe Grüße aus dem Cyberspace

        Tom vom Berg

        --
        Nur selber lernen macht schlau
        http://bergpost.annerschbarrich.de
      2. Hi Dennis,

        vielen Dank für deine ausführlichen Antworten. Alles sehr hilfreich, nur ein paar Sachen noch...

        Alles so, wie ich es erwartet hätte, dein Problem kann ich also nicht nachvollziehen. Vermutlich ist dein Original-Code länger und du hast vielleicht irgendeine wichtige Stelle fürs Posting hier herausgestrichen?

        das Script ist nicht wesentlich länger, weis nur ein Testscript ist. Es kommt lediglich ein Formular hinzu und eine if($_POST....) Anweisunghinzu die das Cookie setzt und dann  noch der Link zum löschen. Leider habe ich ja nun schon dran weiter gebastelt, so dass ich das original nicht mehr habe, werde aber nachher nochmal eine solche komplette Vorlage basteln.

        session_destroy() würde ich gar nicht benutzen, weil es nicht viel bringt. Wie du die Daten einer Session gelöscht kriegst habe ich dir oben schon gezeigt. Was macht session_destroy() mehr? Es löscht noch die Session-Datei auf dem Server (das was du in deinem tmp Ordner siehst), aber das bringt dir nichts, weil du dich nicht darauf verlassen kannst, dass der Besucher sich auch wirklich ausloggt, sprich du hast keine Garantie, dass session_destroy() auch wirklich für jeder erzeugte Session am Ende auch wieder aufgerufen wird. Wenn der Besucher also einfach seinen Browser zu macht hast du in jedem Fall so oder so eine verwaiste Session-Datei auf dem Server.

        Ja das ist klar. Aber ich nutze das auch aus Sicherheitsgründen auch wenn der User sich nicht ausloggt. Wenn zb. Handlungen stattfinden die nicht sollen. Vielleicht auch nur subjektives Empfinden, aber wo keine Sessiondaten, da kann auch nichts mehr fortgesetzt werden.

        Zu deinem hier beschriebenen Problem hat dir Tom ja bereits geschrieben - in deinem PHP-Script löschst du stets zuerst alle Cookies, folglich auch das Session-Cookie, da dieses auch nichts anderes als ein normales Cookie ist.

        Ja hat er, aber ich glaube ihr habt mich falsch verstanden. Daher nochmal:

        User hat Cookies im Browser abgestellt. Wenn PHP nun die Option bietet durch "session.use_only_cookies" heisst das für mich soviel wie, wenn der User keine Cookies akzeptiert, hat er Pech gehabt. Dann wird auch keine Session Datei angelegt, bzw. wenn angelegt dann auch wieder sofort gelöscht.

        Stattdessen erhalte ich mit jedem Aufruf eine neue Datei. Das halte ich für Fatal, denn ich könnte dieses Verhalten ausnutzen indem ich Millionen von Session Dateien in den TMP Ordner produzieren lassen könnte, und das bevor der GC greift.

        Das müsste sich doch extrem negativ auf den Server auswirken, oder sehe ich das falsch?

        Mike

        1. Hello,

          Zu deinem hier beschriebenen Problem hat dir Tom ja bereits geschrieben - in deinem PHP-Script löschst du stets zuerst alle Cookies, folglich auch das Session-Cookie, da dieses auch nichts anderes als ein normales Cookie ist.

          Ja hat er, aber ich glaube ihr habt mich falsch verstanden. Daher nochmal:

          User hat Cookies im Browser abgestellt. Wenn PHP nun die Option bietet durch "session.use_only_cookies" heisst das für mich soviel wie, wenn der User keine Cookies akzeptiert, hat er Pech gehabt. Dann wird auch keine Session Datei angelegt, bzw. wenn angelegt dann auch wieder sofort gelöscht.

          Stattdessen erhalte ich mit jedem Aufruf eine neue Datei. Das halte ich für Fatal, denn ich könnte dieses Verhalten ausnutzen indem ich Millionen von Session Dateien in den TMP Ordner produzieren lassen könnte, und das bevor der GC greift.

          Und genauso habe ich es Dir auch beschrieben.

          Also nochmal ganz langsam zum Mitmeißeln :-) :

          [Start]

          Wenn Du "session_start()" aufrufst, schaut PHP nach, ob es einen Cookie unter dem eingestellten Bezeichner ( = session_name() ) im $_COOKIE-Array findet. Es schaut auch nur noch dort nach, weil Du ja "session.use_only_cookies" auf true gesetzt hast.

          Ist ein Cookie vorhanden, wird nach einer dazu passenden Session-Datei gesucht und bei Erfolg die angefangene Session fortgestzt. Ist kein passender Cookie vorhanden, wird ein neuer ausgewürfelt, dieser in die response-Header eingestzt, eine _neue_ Session-Datei angelegt und darin bei Scriptende das $_SESSION-Array das eben gerade noch aktuellen Scriptes abgespeichert.

          Mit der response landet der Cookie nun erst beim Client. Dieser nimmt ihn aber nicht an (= speichert ihn nicht ab!). Folglich kann beim nächsten Request an die Domain auch kein Cookie mitgeschickt werden und das Spiel beginnt wieder bei [Start].

          Liebe Grüße aus dem Cyberspace

          Tom vom Berg

          --
          Nur selber lernen macht schlau
          http://bergpost.annerschbarrich.de
          1. Hi Tom,

            also doch. Scheint also technisch nicht anders machbar zu sein. Und Dennis, ja genau das meinte ich mit alle Dateien in einem Verzeichnis.

            Ich will aber auch nicht die Vorgabe session.save_path ändern, da ich mal von möglichen Problematiken mit dem GC bei einer Änderung gelesen hatte.

            Gut, für mich persönlich ist das natürlich leicht zu verhindern indem ich ein Testcookie vorher abfrage also teste ob der User überhaupt Cookies annimmt, bevor ich eine Session starte. Das dies natürlich nur beim erneuten Aufruf abgefragt werden kann ist klar. Funktioniert auch prima.

            Trotzdem, mir fiel das nur durch Zufall auf, also gehe ich mal davon aus, dass eine Menge Sessionseiten genau in diese Gefahr laufen könnten, eben das jemand böswillig Millionen Dateien im (dem Einen)Tmpordner produziert.

            Ich bin somit gewarnt. Danke an euch für die Infos.

            Mike

            1. Hello Mike,

              Gut, für mich persönlich ist das natürlich leicht zu verhindern indem ich ein Testcookie vorher abfrage

              das geht z.B. per Weiterleitungs-Header auf eine URL mit Parameter. Achtung, ohne ein zusätzliches Merkmal gibt es schnell einen zirkulären Verlauf. So ähnlich macht das PHP ja selber auch, nur dass die dann als "Trans SID" gleich die Session-ID anhängen.

              session.save_path solltest Du auf alle Fälle in ein Verzeichnis legen, in dem nur Dein Account  lesen kann, wenn Du auf einem shared Host arbeitest. Was sagt denn Dein php-info darüber aus?

              Liebe Grüße aus dem Cyberspace

              Tom vom Berg

              --
              Nur selber lernen macht schlau
              http://bergpost.annerschbarrich.de
            2. echo $begrüßung;

              Ich will aber auch nicht die Vorgabe session.save_path ändern, da ich mal von möglichen Problematiken mit dem GC bei einer Änderung gelesen hatte.

              Eine Problematik mit dem GC gibt es, wenn man den session.save_path auf einer Multiuser-Umgebung _nicht_ ändert und dieser für alle Anwender auf das selbe Verzeichnis zeigt. Dann hat nämlich jeder die Möglichkeit, mit eigenen (aggressiven) GC-Konfigurationen die Sessiondateien der anderen aufräumen zu lassen.

              echo "$verabschiedung $name";

              1. Hello,

                Ich will aber auch nicht die Vorgabe session.save_path ändern, da ich mal von möglichen Problematiken mit dem GC bei einer Änderung gelesen hatte.

                Eine Problematik mit dem GC gibt es, wenn man den session.save_path auf einer Multiuser-Umgebung _nicht_ ändert und dieser für alle Anwender auf das selbe Verzeichnis zeigt. Dann hat nämlich jeder die Möglichkeit, mit eigenen (aggressiven) GC-Konfigurationen die Sessiondateien der anderen aufräumen zu lassen.

                ... oder die Sessiondateien auslesen zu lassen (bei der Modulversion von PHP).

                Liebe Grüße aus dem Cyberspace

                Tom vom Berg

                --
                Nur selber lernen macht schlau
                http://bergpost.annerschbarrich.de
        2. Hi Mike,

          User hat Cookies im Browser abgestellt. Wenn PHP nun die Option bietet durch "session.use_only_cookies" heisst das für mich soviel wie, wenn der User keine Cookies akzeptiert, hat er Pech gehabt. Dann wird auch keine Session Datei angelegt, bzw. wenn angelegt dann auch wieder sofort gelöscht.

          Und genau diese Annahme ist falsch. Du hast auf dem Server keine Möglichkeit festzustellen, ob ein Browser Cookies aktzeptiert oder nicht, zumindest nicht innerhalb eines Requests. Die einzige Möglichkeit festzustellen ob ein Browser Cookies aktzeptiert, ist, ihm ein Cookie zu geben und dann beim *nächsten* Request zu gucken, ob das Cookie vom Browser wieder mit an den Server gesendet wurde.

          Mach dir bitte bewusst, wie session_start() funktioniert, vereinfacht gesagt, macht diese Funktion folgendes:

          if (isset($_COOKIE[session_name()])) {
              $id = $_COOKIE[session_name()];
            }
            else if (session.use_only_cookies deaktiviert && isset($_GET[session_name()])) {
              $id = $_GET[session_name()];
            }
            else {
              $id = Neue zufällige ID erzeugen
              setcookie(session_name(), $id, ...);
            }
            $file = session.save_path . $id
            Datei $file öffnen, falls nicht existent, vorher anlegen

          Wie gesagt, das ist jetzt sehr vereinfacht dargestellt. In Wirklichkeit kommen werden da im PHP-Sourcecode natürlich noch etliche Sicherheitsprüfungen etc. vorhanden sein.

          Worauf ich aber hinaus will: Beim Aufruf von session_start() wird *in jedem Fall* eine Session-Datei geöffnet. Und wenn keine bereits vorhandene Session-Datei verwendet werden kann, weil auf keinem Wege eine Session-ID übermittelt wurde, dann wird eben nun mal eine neue Session-Datei angelegt.

          Stattdessen erhalte ich mit jedem Aufruf eine neue Datei. Das halte ich für Fatal, denn ich könnte dieses Verhalten ausnutzen indem ich Millionen von Session Dateien in den TMP Ordner produzieren lassen könnte, und das bevor der GC greift.

          Wie gesagt: Die Unterstützung von Cookies kannst du nur über zwei Requests hinweg feststellen, dafür muss PHP zwangsläufig eine Session-Datei anlegen, anders funktioniert es nicht.

          Das müsste sich doch extrem negativ auf den Server auswirken, oder sehe ich das falsch?

          Das glaube ich nicht. Es werden ja erst mal nur Session-Dateien angelegt, die sind erst mal leer (sofern du da nicht direkt irgendwelche riesigen Mengen an Daten reinspeicherst) und belegen somit nicht sonderlich viel Festplattenkapazität. Somit glaube ich kaum, dass dir jemand die komplette Festplatte vollgestopft und dein System dadurch zum Absturz bringt.

          Durchaus problematischer ist es, wenn PHP alle Session-Dateien in einen Ordner ablegt. Dann wird das einlesen dieses Ordners nämlich mit der Zeit immer langsamer, weil ein Scan des Ordners über entsprechend mehr Dateien laufen muss. Dafür gibt es aber die Möglichkeit session.save_path so zu konfigurieren, dass PHP noch über Unterordner arbeitet, also die Session abcdefgh in der Datei /tmp/a/b/c/sess_abcdefgh ablegt.

          Viele Grüße,
            ~ Dennis.