Mein remake des Login Systems von MrHappiness

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

  • Mein remake des Login Systems von MrHappiness

    Da das Login System von mrhappiness nun schon ein paar Jahre alt, und somit nicht mehr auf dem aktuellsten Stand ist, habe ich mich mal an einem remake versucht. Wollte ursprünglich nur ein paar Änderungen vornehmen, doch am Ende habe ich es von Grund auf neugeschrieben. Vorweg, ich bin PHP Anfänger (bzw. habe mich mal für ein paar Tagen vor mehr als 4 Jahren damit beschäftigt).

    Könnt ihr mir helfen die grössten Sicherheitslücken zu stopfen? Sticht da was ins Auge was die Logik oder die Sicherheit angeht?
    Ideen, Vorschläge und Kritik ist selbstverständlich willkommen.

    Grundlegende Änderungen (plus meine Gedanken dazu):
    - Als MySQL Schnittstelle wird nun PDO anstatt mysql_verwendet
    - Anstatt md5 wird sha1 verwendet
    - Passwörter werden zusätzlich mit einem Salt gespeichert
    - Cookie Funktion für automatische Anmeldung hinzugefügt. (alle Informationen im Cookie werden mit sha1 gehasht und beinhalten einen Salt. Anstatt das gehashte Passwort im Cookie zuspeichern, wurde ein extra CookieSession Feld in der DB angelegt.)
    - Logout Funktion keine seperate Datei mehr, sonder über $_GET['logout'] aufrufbar.

    Habe versucht den Code ein bisschen zu dokumentieren, und falls der Codeschnipsel nicht von mir stammt habe ich die Quelle angegeben.

    login.php
    PHP-Code:
    <?php
    session_start
    ();

    include_once(
    'functions.inc.php');

    $username $_POST['username'];
    $password $_POST['password'];
    $autologin $_POST['autologin'];
    $cookieusername $_COOKIE['cookieusername'];
    $cookiesession $_COOKIE['cookiesession'];

    try {
        
    //Open MySQL Connection with PDO
        
    $dbh = new PDO('mysql:host='.$dbhost.';dbname='.$dbname.''$dbuser$dbpass);

        
    //If logout is requested
        
    if ($_GET['logout'] === "yes") {
            
    logout();
            
    $cookieusername NULL;
            
    $cookiesession NULL;
            echo 
    'Sie wurden erfolgreich ausgeloggt.<br />';
        }

        
    //If logged in
        
    if (IsLoggedIn() === true) {
            echo 
    'Sie sind zurzeit eingeloggt.<br /><a href="?logout=yes">Logout</a>';
        }

        
    //If Cookie was found and verified as true, login user
        
    elseif (isset($cookieusername) && isset($cookiesession)) {
            
    $UserID VerifyCookie($cookieusername$cookiesession);
            
    //If cookie information incorrect, error message and logout user to delete cookie
            
    if ($UserID === false) {
                
    logout();
                echo 
    'Automatische Anmeldung durch Cookie fehlgeschlagen.<br />
    Sie werden nun ausgeloggt und die Cookies werden gelöscht</br />'
    ;
            }

            
    //If cookie information okay, login user
            
    else {
                
    LoginUser($UserID$autologin);
                echo 
    'Willkommen zurück. Sie wurden automatisch durch ihre Cookies eingeloggt.<br />';
            } 
        }

        
    //Login, continue only if Username and Password are not empty
        
    elseif (isset($_POST['login']) && $username !== "" && $password !== "") {    
            
    $UserID VerifyUser($username$password);
            
    //If login incorrect, error message
            
    if ($UserID === false) {    
                echo 
    'Anmeldung fehlgeschlagen.<br />';
            }

            
    //If login okay, login user
            
    else {
                
    LoginUser($UserID$autologin);
                echo 
    'Sie wurden erfolgreicht eingeloggt.<br />';

            }
        }

        
    //If not logged in, display login form    
        
    if (IsLoggedIn() === false) {
            echo 
    '<form method="post" action="login.php">
                  Username: <input type="text" name="username" id="username" /><br />
                  Password: <input type="password" name="password" id="password" /><br />
                  Angemeldet bleiben: <input type="checkbox" name="autologin" id="autologin" /><br />
                  <input name="login" type="submit" value="Login">
                  </form>'
    ;
        }

        
    //Close MySQL Connection
        
    $dbh NULL;
    }

    catch(
    PDOException $e) {
        echo 
    $e->getMessage();
    }    
    ?>
    functions.inc.php
    PHP-Code:
    <?php
    //Database login information
    $dbhost "HOSTNAME";
    $dbname "DATABASE NAME";
    $dbuser "USERNAME";
    $dbpass "PASSWORD"


    //Disbaling Magic Quotes, code from http://de.php.net/manual/de/security.magicquotes.php#76387
    if (get_magic_quotes_gpc()) {
        function 
    stripslashes_deep($value)
        {
            
    $value is_array($value) ?
                        
    array_map('stripslashes_deep'$value) :
                        
    stripslashes($value);

            return 
    $value;
        }

        
    $_POST array_map('stripslashes_deep'$_POST);
        
    $_GET array_map('stripslashes_deep'$_GET);
        
    $_COOKIE array_map('stripslashes_deep'$_COOKIE);
        
    $_REQUEST array_map('stripslashes_deep'$_REQUEST);
    }

    //Check if logged in
    function IsLoggedIn() {
        global 
    $dbh;
        
        
    $sql='SELECT session FROM users WHERE session="'.session_id().'"';
        
    $row=$dbh->query($sql)->fetch();
        
        if (
    session_id() === $row['session']) {
            return 
    true;
        }
        
        else {
            return 
    false;
        }
    }

    //Veryfing POST_Username and POST_Password
    function VerifyUser($username$password) {
        global 
    $dbh;
        
        
    $sql 'SELECT UserID, password, salt FROM users WHERE username = :username';
        
    $stmt $dbh->prepare($sql);
        
    $stmt->bindParam(':username'$usernamePDO::PARAM_STR40);
        
    $stmt->execute();
        
    $row $stmt->fetch();
        
        if (
    $row['password'] === sha1($password.$row[salt]))    {
            return 
    $row[UserID];
        }
        
        else {
            return 
    false;
        }
    }

    //Logging in User
    function LoginUser($UserID$autologin) {
        global 
    $dbh;
        
        
    $sql='UPDATE users SET session="'.session_id().'" WHERE UserID = "'.$UserID.'"';
        
    $dbh->exec($sql);
        
        
    //Set a Cookie and Update DB with CookieSession
        
    if ($autologin == true) {
            
            
    $sql 'SELECT username, salt FROM users WHERE UserID = "'.$UserID.'"';
            
    $row $dbh->query($sql)->fetch();
            
            
    $cookiesalt sha1(GenerateSalt());
            
    $usernamesalt sha1($row['username'].$row['salt']);
            
            
    setcookie("cookieusername"$usernamesalttime() + 31536000"/");
            
    setcookie("cookiesession"$cookiesalttime() + 31536000"/");
        
            
    $sql 'UPDATE users SET cookiesession="'.$cookiesalt.'" WHERE UserID = "'.$UserID.'"';
            
    $dbh->exec($sql);
            }
    }

    //Creating Salt with 40 characters, code from http://wiki.jumba.com.au/wiki/PHP_Generate_random_password
    function GenerateSalt() {
        
    $characters "01234567890abcdefghijkmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_!?-+";
        
    $i 0;
        
    $salt "";
        while (
    $i <= 40) {
            
    $salt .= $characters{mt_rand(0,strlen($characters))};
            
    $i++;
        }
        return 
    $salt;
    }

    //Verify Cookie
    function VerifyCookie($cookieusername$cookiesession) {
        global 
    $dbh;
        
        
    $sql 'SELECT UserID, username, salt, cookiesession FROM users WHERE cookiesession = :cookiesession';
        
    $stmt $dbh->prepare($sql);
        
    $stmt->bindParam(':cookiesession'$cookiesessionPDO::PARAM_STR40);
        
    $stmt->execute();
        
    $row $stmt->fetch();
        
        if (
    $cookiesession === $row['cookiesession'] && $cookieusername === sha1($row['username'].$row['salt']))    {
            return 
    $row[UserID];
        }
        
        else {
            return 
    false;
        }
    }

    //Logout Users, delete cookie and set session & cookiesession field in DB to NULL
    function Logout() {
        global 
    $dbh;
        
        
    setcookie("cookieusername"""time() - 31536000"/");
        
    setcookie("cookiesession"""time() - 31536000"/");
        
        
    $dbh->exec('UPDATE users SET session="'.NULL.'", cookiesession = "'.NULL.'" WHERE session = "'.session_id().'"');
    }

    ?>

    Hier die dazugehörige SQL Struktur (inkl. 3 Test Users, admin, test, test2 - Username und Passwort jeweils gleich)
    Code:
    CREATE TABLE `users` (
      `UserID` int(11) unsigned NOT NULL auto_increment,
      `username` varchar(40) NOT NULL,
      `password` varchar(40) NOT NULL,
      `salt` varchar(40) NOT NULL,
      `session` varchar(32) default NULL,
      `cookiesession` varchar(40) default NULL,
      `email` varchar(255) NOT NULL,
      PRIMARY KEY  (`UserID`),
      UNIQUE KEY `username` (`username`,`email`)
    ) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=4 ;
    Code:
    INSERT INTO `users` (`UserID`, `username`, `password`, `salt`, `session`, `cookiesession`, `email`) VALUES 
    (1, 'admin', '67440a4fc2736f883108ae1c69dab0606222e0cb', 
    'FUgRO;1txF,Q;,2qyyR0.yHPOPVTfzegVAIR?ZKc', '', '', 'admin@admin.com'),
    (2, 'test', '4307bbda209f29f932e8f0a05baf7dc8f8a3015b', 
    '!tWcGU1GyHW68N-4LcZdHg9XhW!FR,KdzgY6nORV', '', '', 'test@test.de'),
    (3, 'test2', '79475350f67f95b32f18a3dfb071af4cc6f20f96', 
    'ER0E6tiP0vNMoz0aOLt6hU2wYE,V!Bg6eEQf7iz;', '', '', 'test2@test2.de');
    UserID // Selbsterklärend
    username // Selbsterklärend
    password // sha1 ($EingabeDesUsers.$Salt)
    salt //Zufällig generierter 40 Stelliger String
    session // aktuelle session_id()
    cookiesession // sha1 (Zufällig generierter 40 Stelliger String)
    email // Selbsterklärend


    Freundliche Grüsse
    Londrag
    Zuletzt geändert von Londrag; 06.04.2008, 22:20.

  • #2
    bitte code umbrechen.
    schaut ganz gut aus.
    blllubb

    Kommentar


    • #3
      Tssss ... Es gibt eine Klasse von äh ... Haxe, die auf dem TUT von Happy aufbaut.

      Heut' zu Tage, wenn du schon abkupferst, dann lieber richtig und zwar mit PHP5 OOP, dann wird sich vielleicht jemand dafür interessieren, aber so bestimmt nicht.

      Nachtrag: global? Igit!
      Zuletzt geändert von asp2php; 06.04.2008, 21:10.

      Kommentar


      • #4
        Original geschrieben von frodenius
        bitte code umbrechen.
        schaut ganz gut aus.
        Sorry, benutze eine 1920x1200er Auflösung, daher fällt es mir nicht immer auf. Code war eigentlich Gebrochen, nur jedes Mal wenn ich den Beitrag editere werden die URL wieder automatisch verlinkt.

        Original geschrieben von asp2php
        Tssss ... Es gibt eine Klasse von äh ... Haxe, die auf dem TUT von Happy aufbaut.

        Heut' zu Tage, wenn du schon abkupferst, dann lieber richtig und zwar mit PHP5 OOP, dann wird sich vielleicht jemand dafür interessieren, aber so bestimmt nicht.

        Nachtrag: global? Igit!

        Schade eigentlich, dachtedass ich auf einem gutem Weg sei. Ich habe es ja in erster Linie nicht für Andere geschrieben, sondern für mich.
        Wie gesagt, ich bin Anfänger und es ist nur ein kleines Hobby. Habe gerade die letzten 2 Wochen damit verbracht mich auf den aktuelleren Stand zu bringen und mich mit PDO auseinander gesetzt.

        Um OOP zulernen wird es wohl noch länger dauern. Aber es kann ja nicht schaden den Code mal hier hin zu stellen und euch zu fragen wie es mit der Sicherheit aussieht.

        Kommentar


        • #5
          Original geschrieben von asp2php
          Heut' zu Tage, wenn du schon abkupferst, dann lieber richtig und zwar mit PHP5 OOP, [...]
          Ich würde nicht sagen, dass OOP ein Qualitätsmerkmal ist. Das wird von vielen einfach sehr überschätzt denke ich.

          Drupal z.b. hat sich AFAIK ganz bewusst gegen OOP entschieden.

          Kommentar


          • #6
            Es muss nicht alles OOP sein, aber gerade dieser Vorgang (~Usermanagement) ist dafür ganz geeignet.

            Kommentar


            • #7
              Ist global einfach unschön oder besteht dadurch ein Sicherheitsrisiko?

              Kommentar


              • #8
                global braucht niemand, früher nicht, jetzt auch nicht, ich weiss nicht welcher Idiot auf die Idee kam, dies überhaupt zu ermöglichen; ich spreche nicht nur wegen PHP sondern in alle Programmiersprache. Eine Funktion/Procedure/Klasse muss in sich gekapselt sein, dass alle externe Daten immer via Parameter übergeben müssen, quasi als Bedingung. Denn nur somit ist die Wiederverwendbarkeit gewährleistet, d.h. du kannst sie in beliebige Projekte übenehmen und sofort verwenden.

                Kommentar


                • #9
                  OffTopic:
                  Ich würde nicht sagen, dass OOP ein Qualitätsmerkmal ist. Das wird von vielen einfach sehr überschätzt denke ich.

                  Drupal z.b. hat sich AFAIK ganz bewusst gegen OOP entschieden.
                  Das sehe ich ganz anders. Eine Klasse ist genau das richtige, um zusammenhängende Funktionen und Aufgaben gemeinsam zu kapseln. Warum Drupal sich dagegen entschieden hat, ist mir beim besten Willen nicht klar!

                  Kommentar


                  • #10
                    Okay verstehe, danke für die Aufklärung. Man muss ja aber nicht jeden einzelnen Codeschnipsel der in der Welt rumfliegt wieder verwenden können, manche sind halt einfach auf ein bestimmtes Script fixiert Und $dbh bezieht sich ja auf das PDO __construct, was in diesem Script so oder so Voraussetzung ist.

                    Kommentar


                    • #11
                      hier wird doch wohl nicht ernsthaft über Drupal geredet oder? Finde überhaupt nichts gutes an diesem System.
                      Webdesign und Webentwicklung - Plunix.de

                      Kommentar


                      • #12
                        hier wird doch wohl nicht ernsthaft über Drupal geredet oder? Finde überhaupt nichts gutes an diesem System.
                        Diese Meinung ist sicherlich subjektiv, sehe ich aber genau so wie du.

                        Kommentar


                        • #13
                          Original geschrieben von asp2php
                          Tssss ... Es gibt eine Klasse von äh ... Haxe, die auf dem TUT von Happy aufbaut.

                          Heut' zu Tage, wenn du schon abkupferst, dann lieber richtig und zwar mit PHP5 OOP, dann wird sich vielleicht jemand dafür interessieren, aber so bestimmt nicht.

                          Nachtrag: global? Igit!
                          Kann mir bitte jemand sagen wo ich diese Klasse finde?
                          Suche mich schon dumm und dämlich

                          Merci
                          kesandal

                          Kommentar

                          Lädt...
                          X