Ankündigung

Einklappen
Keine Ankündigung bisher.

"Eingeloggt bleiben"-Cookie Sicherheitsproblem

Einklappen

Neue Werbung 2019

Einklappen
X
  • Filter
  • Zeit
  • Anzeigen
Alles löschen
neue Beiträge

  • "Eingeloggt bleiben"-Cookie Sicherheitsproblem

    Hallo liebe php.de-Community,

    aktuell arbeite ich an einem "Eingeloggt bleiben"-System. Soweit funktioniert das auch, allerdings ist mir jetzt eine Sicherheitslücke aufgefallen.

    Ablauf: Ein Hash wird in der Nutzerdatenbank gespeichert. Durch diesen wird später identifiziert, ob der Nutzer bereits eingeloggt war. Sollte ein Nutzer sich nun über das Loginformular einloggen und die "Angemeldet bleiben"-Checkbox aktivieren wird, falls noch nicht im Datensatz, ein Hash in die Datenbank geschrieben. Falls ein Hash dem Nutzer bereits zugewiesen war, wird dieser aus der Datenbank genommen und bei beiden Wegen als Cookie auf dem Rechner gespeichert. Bei einem Besuch des Loginbereiches wird dann geprüft, ob ein "Eingeloggt bleiben"-Cookie gesetzt ist. Wenn ja, wird in der Nutzerdatenbank nach diesem Hash, welcher in dem Cookie steht, gesucht. Falls gefunden, wird der Nutzer durch den Cookie eingeloggt.

    Problem: Sollte ein Fremder Zugriff auf die Cookies eines "Angemeldet bleiben"-Nutzers erhalten, dann kann dieser den Hash in dem Cookie kopieren und in seinem Browser speichern (gibt z.B. Cookie-AddOns zum Ändern und Hinzufügen von Cookies). Besucht dieser dann die betreffende Webseite/Loginbereich, dann wird dieser einfach eingeloggt, obwohl er nicht der Besitzer des Accounts ist und sich auch noch nie mit Kennwort + Nutzername eingeloggt hat.

    Meine Frage: Wie sollte das Script erweitert werden, um zu erreichen, dass diese Sicherheitslücke geschlossen wird? Das muss man irgendwie nochmal extra validieren können. Hat jemand eine Idee, wie ich das machen könnte?

    StayLoggedIn im Formular

    PHP-Code:
    [...]
    $id $_SESSION['id'];

    if (isset(
    $_POST['stayloggedin'])){
    // Die SESSION ist bereits durch das Einloggen zugewiesen > $id
       
    $statement $pdo->prepare("SELECT * FROM nutzer WHERE id = '$id'");
       
    $result $statement->execute(array('stayloggedin'));
       
    $slidata $statement->fetch();

       
    $db_stayloggedin $slidata['stayloggedin'];

    // Falls noch kein Hash in dem Datensatz "stayloggedin" des Nutzers
       
    if ($db_stayloggedin === '') {

    // Zufällige Zeichenfolge
         
    $zufallcode bin2hex(mcrypt_create_iv(32MCRYPT_DEV_URANDOM));

    // ... wird als Cookie gesetzt
         
    setcookie('stayloggedin'$zufallcodetime() + (86400 30), '/');

    // ... und in den Datensatz geschrieben
         
    $sql "UPDATE nutzer SET stayloggedin='$zufallcode' WHERE id='$id'";
         
    $stmt $pdo->prepare($sql);
         
    $stmt->execute();

       } else {
    // falls bereits ein Datensatz vorhanden ist, wird der Zufallscode aus der DB ausgelesen und als Cookie gespeichert
         
    setcookie('stayloggedin'$db_stayloggedintime() + (86400 30), '/');
       }


    Prüfung im Nutzerbereich
    PHP-Code:
    // Wenn der Stayloggedin Cookie gesetzt ist
    if (isset($_COOKIE['stayloggedin'])){
       
    $stayloggedin $_COOKIE["stayloggedin"];

    // Nutzerdatenbank nach Inhalt des Cookies durchsuchen
       
    $statement $pdo->prepare("SELECT * FROM nutzer WHERE stayloggedin = '$stayloggedin'");
       
    $result $statement->execute(array('stayloggedin'));
       
    $daten $statement->fetch();

       
    $stayloggedindb $daten['stayloggedin'];
       
    // Nutzer ID
       
    $id $daten['id'];

    // Wenn der Hash aus dem Cookie mit dem in der Datenbank übereinstimmt ...
        
    if($stayloggedin == $stayloggedindb){
    // ... dann Cookie erneuern
           
    setcookie('stayloggedin'$stayloggedintime() + (86400 30), '/');
    // ... und Session setzen, Nutzer einloggen
           
    $_SESSION['id'] = $daten['id'];
         }


  • #2
    https://de.wikipedia.org/wiki/Sessio...ma.C3.9Fnahmen
    The string "()()" is not palindrom but the String "())(" is.

    Debugging: Finde DEINE Fehler selbst! | Gegen Probleme beim E-Mail-Versand | Sicheres Passwort-Hashing | Includes niemals ohne __DIR__
    PHP.de Wissenssammlung | Kein Support per PN

    Kommentar


    • #3
      Abgesehen vom Hijacking-Problem hast du auch noch haufenweise Sicherheitslücken im Code. z.B. beachtest du den Kontextwechsel nach SQL nicht. Bei dem Code kann man sich als beliebiger User einloggen, ganz ohne ein fremdes Cookie zu haben.

      Und SELECT * sollte man auch nicht verwenden.

      Kommentar


      • #4
        PDO zu nutzen ist schon ok, du solltest dir noch mal anschauen, wie PDO:: prepare richtig benutzt wird.

        Kommentar


        • #5
          Siehe auch: https://php-de.github.io/jumpto/pdo/...red-statements
          The string "()()" is not palindrom but the String "())(" is.

          Debugging: Finde DEINE Fehler selbst! | Gegen Probleme beim E-Mail-Versand | Sicheres Passwort-Hashing | Includes niemals ohne __DIR__
          PHP.de Wissenssammlung | Kein Support per PN

          Kommentar


          • #6
            Hallo zusammen,

            erstmal Danke für eure Antworten.

            Zitat von jspit Beitrag anzeigen
            PDO zu nutzen ist schon ok, du solltest dir noch mal anschauen, wie PDO:: prepare richtig benutzt wird.
            Okay danke für den Tipp und die Links, ich werde mir die Prepared Statements nochmal anschauen und diese dann verbessern.


            Zitat von hellbringer Beitrag anzeigen
            Abgesehen vom Hijacking-Problem hast du auch noch haufenweise Sicherheitslücken im Code. z.B. beachtest du den Kontextwechsel nach SQL nicht. Bei dem Code kann man sich als beliebiger User einloggen, ganz ohne ein fremdes Cookie zu haben.

            Und SELECT * sollte man auch nicht verwenden.
            Was meinst du denn genau mit dem Kontextwechsel?
            Ich weiß nicht, was du genau mit dem "Bei dem Code kann man sich als beliebiger User einloggen" meinst? Meinst du, dass die Passwort-Verifizierung fehlt (etc)? Das habe ich nicht mit in den Codeschnipsel geschrieben, da es mir (aktuell) um das "Eingeloggt bleiben" geht.

            Beim SELECT * sollte man also eher darauf zurückgreifen, nur die Spalten auszulesen, welche man letztendlich auch benötigt, stimmt's?

            Danke und viele Grueße

            Kommentar


            • #7
              Was hellbringer meinte: der Nutzer kann durch ein modifiziertes Cookie deine SQL-Abfrage manipulieren, da diese nicht ausreichen geschützt ist und den Inhalt des Cookies einfach als Teil der SQL-Abfrage einsetzt. Wenn dort nun bspw ein Single-Quote drinsteht, verursacht das bereits eine fehlerhafte SQL-Abfrage.
              [SIZE="1"]Atwood's Law: any application that can be written in JavaScript, will eventually be written in JavaScript.[/SIZE]

              Kommentar


              • #8
                Zitat von lottikarotti Beitrag anzeigen
                Was hellbringer meinte: der Nutzer kann durch ein modifiziertes Cookie deine SQL-Abfrage manipulieren, da diese nicht ausreichen geschützt ist und den Inhalt des Cookies einfach als Teil der SQL-Abfrage einsetzt. Wenn dort nun bspw ein Single-Quote drinsteht, verursacht das bereits eine fehlerhafte SQL-Abfrage.

                Müsste nun nicht das Problem eines modifizierten Cookies zur Manipulation der SQL-Abfrage verhindert sein (Code unten abgeändert)? Die SQL Abfragen habe ich anhand der Erklärung unter https://php-de.github.io/jumpto/pdo/...red-statements umgesetzt, was von hausi erwähnt wurde. Zusätzlich habe ich mich an den Ratschlag von hellbringer gehalten, SELECT * nicht zu verwenden, sondern explizite Angaben.




                StayLoggedIn im Formular

                PHP-Code:
                [...]
                $id $_SESSION['id'];

                // Die SESSION ist bereits durch das Einloggen zugewiesen > $id
                // Somit ist der Nutzer theoretisch bereits eingeloggt, aber das Skript prüft vor dem 'Redirect' zum Userbereich noch, ob der User StayLoggedIn bleiben möchte

                if (isset($_POST['stayloggedin'])){

                        
                $sql "SELECT id, stayloggedin FROM nutzer WHERE id = :id";
                        
                $stmt $pdo->prepare($sql);
                        
                $aParams = array(':id' => $id);
                        
                $stmt->execute($aParams);
                        
                $row $stmt->fetch();

                        
                $db_stayloggedin $row['stayloggedin'];

                // Falls noch kein Hash in dem Datensatz "stayloggedin" des Nutzers
                   
                if ($db_stayloggedin === '') {

                // Zufällige Zeichenfolge
                     
                $zufallcode bin2hex(mcrypt_create_iv(32MCRYPT_DEV_URANDOM));

                // ... wird als Cookie gesetzt
                     
                setcookie('stayloggedin'$zufallcodetime() + (86400 30), '/');

                // ... und in den Datensatz geschrieben

                        
                $sql "UPDATE nutzer SET stayloggedin = :zufallcode WHERE id = :id";
                        
                $stmt $pdo->prepare($sql);
                        
                $aParams = array(':id' => $id':zufallcode' => $zufallcode);
                        
                $stmt->execute($aParams);

                   } else {
                // falls bereits ein Datensatz vorhanden ist, wird der Zufallscode aus der DB ausgelesen und als Cookie gespeichert
                     
                setcookie('stayloggedin'$db_stayloggedintime() + (86400 30), '/');
                   }


                Prüfung im Nutzerbereich (beim Seitenbesuch)
                PHP-Code:
                // Wenn der Stayloggedin Cookie gesetzt ist
                if (isset($_COOKIE['stayloggedin'])){
                   
                $stayloggedin $_COOKIE["stayloggedin"];

                // Nutzerdatenbank nach Inhalt des Cookies durchsuchen

                        
                $sql "SELECT id, stayloggedin FROM nutzer WHERE stayloggedin= :stayloggedin";
                        
                $stmt $pdo->prepare($sql);
                        
                $aParams = array(':stayloggedin' => $stayloggedin);
                        
                $stmt->execute($aParams);
                        
                $row $stmt->fetch();

                        
                $stayloggedindb $row['stayloggedin'];
                        
                $id $row['id'];

                // Wenn der Hash aus dem Cookie mit dem in der Datenbank übereinstimmt ...
                    
                if($stayloggedin == $stayloggedindb){
                // ... dann Cookie erneuern
                       
                setcookie('stayloggedin'$stayloggedintime() + (86400 30), '/');
                // ... und Session setzen, Nutzer einloggen
                       
                $_SESSION['id'] = $id;
                     }

                Kommentar


                • #9
                  Ist es so wie nun oben verändert denn richtig mit den Prepared Statements? Fallen euch noch weitere Sicherheitslücken auf?

                  Und wie könnte ich denn jetzt das Problem lösen, dass wenn man den Hash in dem Cookie von jmd. anderen "klaut" und dann bei sich im Browser nutzt, dass man dann einfach eingeloggt wird? Zwischenprüfung irgendwie möglich?

                  Danke

                  Kommentar


                  • #10
                    Hast du den Link in Beitrag #2 gesehen.
                    Mein Tipp: lesen.

                    Kommentar


                    • #11
                      Und wie könnte ich denn jetzt das Problem lösen, dass wenn man den Hash in dem Cookie von jmd. anderen "klaut" und dann bei sich im Browser nutzt, dass man dann einfach eingeloggt wird? Zwischenprüfung irgendwie möglich?
                      Hatten wir doch oben in #2 schon.. https zB war eine Variante.
                      The string "()()" is not palindrom but the String "())(" is.

                      Debugging: Finde DEINE Fehler selbst! | Gegen Probleme beim E-Mail-Versand | Sicheres Passwort-Hashing | Includes niemals ohne __DIR__
                      PHP.de Wissenssammlung | Kein Support per PN

                      Kommentar


                      • #12
                        HTTPS ist bereits genutzt. Ist das Skript wie oben demnach also sicher mit den Statements und der HTTPS-Variante oder gibt es noch Ideen zur Verbesserung?

                        Was ich allerdings auch meine ist, dass wenn Person A bei Person B die Cookies ausliest (an dem PC selbst oder über eine Software/Viren/...), dann kann Person A den Inhalt des Cookies abfragen. Anschließend kann er bei sich einen gleichnamigen Cookie mit dem Inhalt der Person B erstellen und ist dann in dem Account von Person B.

                        Schließlich wäre dieser Code in dem Stay Logged In Cookie ja prinzipiell das Gleiche, als würde man in den Cookie Kennwort und Nutzername speichern => man wird eingeloggt, wenn man die Daten hat. Gibt es hierbei nicht noch einen sichereren Weg, um das zu verhindern? Oder gibt es dort keine Möglichkeit?

                        Es handelt sich ja dann hierbei um das unsichere "gemeinsame Geheimnis" (https://de.wikipedia.org/wiki/Sessio...ma.C3.9Fnahmen), oder habe ich das falsch verstanden? Wie könnte die "Challenge-Response-Authentifizierung" umgesetzt werden? Aber... ist die Challenge-Response-Authentifizierung nicht auch => das gemeinsame Geheimnis?

                        Kommentar


                        • #13
                          Wichtig neben Prepared Staments ist in Hinblick auf Session-Klau das hier: https://de.wikipedia.org/wiki/Sessio...Ma.C3.9Fnahmen . Dem solltest du Anwendungsweit vorher ein Auge schenken, (hast du?) bevor du dich um sonstige kryptografische Maßnahmen kümmerst.

                          Wenn du da solche Sorgen hast würde ich dir empfehlen a) ein "stay logged in" nicht anzubieten, oder b) dich mit Dienstleiter für Sicherheitstechniken dahingehend in Verbindung zu setzen.

                          wenn Person A bei Person B die Cookies ausliest (an dem PC selbst oder über eine Software/Viren/...),
                          Wenn jemand "böser" physischen Kontakt zu deinem PC hatte oder du dir einen Virus eingefangen hat, dann ist sowieso alles möglich und das Cookie eines deiner geringeren Probleme. Denk alleine an die gespeicherten Passwörter im Browser, Keylogger, etc etc.. Aber da schweifen wir jetzt wohl ab.

                          Meine Meinung.
                          The string "()()" is not palindrom but the String "())(" is.

                          Debugging: Finde DEINE Fehler selbst! | Gegen Probleme beim E-Mail-Versand | Sicheres Passwort-Hashing | Includes niemals ohne __DIR__
                          PHP.de Wissenssammlung | Kein Support per PN

                          Kommentar


                          • #14
                            Zitat von hausl Beitrag anzeigen
                            Wichtig neben Prepared Staments ist in Hinblick auf Session-Klau das hier: https://de.wikipedia.org/wiki/Sessio...Ma.C3.9Fnahmen . Dem solltest du Anwendungsweit vorher ein Auge schenken, (hast du?) bevor du dich um sonstige Kryptografische Maßnahmen kümmerst.
                            Meinst du damit die Verwendung von der verschlüsselten Datenübertragung durch HTTPS (SSL, ..)?


                            Zitat von hausl Beitrag anzeigen
                            Wenn du da solche Sorgen hast würde ich dir empfehlen a) ein "stay logged in" nicht anzubieten, oder b) dich mit Dienstleiter für Sicherheitstechniken dahingehend in Verbindung zu setzen.
                            Ok, danke - eine Frage wie du es machen würdest: Hättest du die Möglichkeit mit dem Stay Logged In denn ähnlich wie ich umgesetzt, oder wärst du da ganz anders vorgegangen?


                            Zitat von hausl Beitrag anzeigen
                            Wenn jemand "böser" physischen Kontakt zu deinem PC hatte oder du die einen Virus eingefangen hat, dann ist sowieso alles möglich. Denk alleine an die gespeicherten Passwörter im Browser, Keylogger, etc etc.. Aber da schweifen wir jetzt wohl ab.
                            Da hast du Recht. Da habe ich so noch gar nicht dran gedacht.

                            Kommentar


                            • #15
                              Du kannst dich nur vor Session Hijacking schützen, indem du es den Angreifen so schwer wie möglich machst, an gültige Cookies anderer Nutzer heranzukommen. Die beliebteste Methode ist dabei Cross-Site-Scripting. Damit dein Login sicher ist, muss also auch deine Anwendung insgesamt sicher sein. Du kannst die Cookies natürlich auch mit einem Ablaufdatum versehen, sodass ein erneuter, automatischer Login nach bspw. 24h nicht mehr möglich ist. Ist halt eine miese UX, aber macht es Angreifern (die ggf. sogar physischen Zugriff haben) etwas schwerer.
                              [SIZE="1"]Atwood's Law: any application that can be written in JavaScript, will eventually be written in JavaScript.[/SIZE]

                              Kommentar

                              Lädt...
                              X