Sichere Zugriffsklasse?

Danielku15

Erfahrenes Mitglied
Hi Leute.

Thema Sicherheit sollte man bei einem Login System auf jeden Fall ganz groß schreiben. Jedoch bin ich in dieser Hinsicht schon wieder eingerostet da sich in letzter Zeit (seit meinem letzten größeren Projekt) sicher einiges getan hat. Jetzt wollte ich euch Fragen was ihr in Sachen Sicherheit von dieser ZugriffsSchutz-Klasse haltet. Ich denke auch daran ein schönes Tutorial dafür zu schreiben, jedoch zuvor sollte Sie nicht unbedingt Fehlerhaft sein:

PHP:
<?php
/**
 * Dies ist eine Klasse welche 
 *
 */
class ZugriffsSchutz
{
    /**
     * Leerlaufzeit in Minuten
     *
     * @var unsigned int 
     */
    var $idle=30;
    
    /**
     * Logoutseite
     *
     * @var string
     */
    var $logout_site = 'index.php?site=logout';
    
    /**
     * Seite auf die Nach dem Login / -out verwiesen wird
     *
     * @var string
     */
    var $log_target = 'index.php?site=start';
    
    /**
     * Loginseite
     * %eid für die FehlermeldungsID
     *  0=> Nicht Eingeloggt / Keine Berechtignung
     *     1=> Falscher Benutzername    
     *  2=> Falsches Passwort
     *  3=> Zu Lange Inaktiv
     *  4=> Erfolgreich Eingeloggt
     *
     * @var string
     */
    var $login_site = 'index.php?site=login&eid=%eid';
    
    /**
     * MySQL Tabelle der User
     *
     * @var string
     */
    var $user_table = 'users';
    
    /**
     * Prefix der Cookies
     *
     * @var string
     */
    var $cookie_prefix = 'mySite_';
    
    /**
     * Modus der Seite 
     *      1=> Zugriffsschutz
     *         2=> Login
     *         3=> Logout
     *
     * @var 1/2/3
     */
    var $modus = 1;

    /**
     * Instanziert ein Neues Objekt der Klasse 
     *
     * @param int $modus 1-> Zugriffsschutz / 2 => Login  / 3=> Logout 
     * @param int $mindestrecht Mindestrecht um die Seite zu Betreten.
     */
    function ZugriffsSchutz($modus=1, $mindestrecht=1)
    {    
        // Modus Festlegen
        $this->modus = $modus;    
            
        /////////////////////////////////////////////////
        /// Forular Daten Testen
        /////////////////////////////////////////////////
        
        // Logout Formular Bestätigt?
        if($_POST['FormType'] == 2)
        {
            // Logout ausführen
            $this->logout();
            header('Location:' . $this->log_target);
        }

        // Login Formular Bestätigt?
        if($_POST['FormType'] == 1)
        {
            // Login Daten überprüfen
            $ret = $this->testlogin();
            // Login Seite und eingeloggt?
            if($this->modus == 2 && $ret[0])
            {
                // Auf Zielseite umleiten
                header('Location:' . $this->log_target);
            }
        }

        /////////////////////////////////////////////////
        /// Modi testen 
        /////////////////////////////////////////////////
        
        // Seitenschutz?
        if($this->modus==1)
        {
            // Daten auf Mindestdaten überprüfen
            $ret = $this->testrecht($mindestrecht);
        }

        // Eingeloggt?
        if($ret[0])
        {
            // Session neu Schreiben
            $this->schreibrecht();
        }

        // Nicht eingeloggt und Keine Logout Seite aufgerufen
        if(!$ret[0] && $this->modus!=3)
        {
            // Loginseite anzeigen
            header('Location:' . str_replace('%eid', $ret[1], $this->login_site));
        }

        // Nicht eingeloggt und Logout Seite Aufgerufen
        if(!$ret[0] && $this->modus==3)
        {
            // Logout Ziel aufrufen
            header('Location:' . $this->umleitenlogout);
        }
    }

    /**
     * Überprüft Die Daten bei einem Login
     *
     * @return 0=> Eingeloggt true/false 1=> Fehlermeldung int
     */
    function testlogin()
    {
        // User aus der DB holen
        $db = new MySQLConnection();
        $result=$db->db_sql('SELECT *, (UNIX_TIMESTAMP()-UNIX_TIMESTAMP(`LastActivityTime`)) as `TimeDiff`, FROM `"'.$this->user_table.'"` WHERE `Username`="'.mysql_escape_string($_POST['Username']).'";');
    
        // Existiert nur einer
        if($db->anz != 1 )
        {
            // Nicht eingeloggt / Ungültiger Benutzername 
            return array(false, 1);
        }
    
        // Stimmt das Kennwort überein?
        if(md5($_POST['Password']) != $result[0]['Password'])
        {
            // Nicht eingeloggt / Ungültiges Passwort
            return array(false, 2);
        }
    
        // Neue Session ID dem User Zuweisen
        $query = 'UPDATE `'.$this->user_table.'` SET `LastActivityTime`=NOW(), `SID`="'.session_id().'" WHERE `UID`="'.$result[0]['UID'].'";';
        $db->db_sql($query);
        
        // Sessiondaten Schreiben        
        $_SESSION[$this->cookie_prefix.'UID'] = $result[0]['UID'];
        $_SESSION[$this->cookie_prefix.'SID'] = session_id();
        return array(true, 4);
    }

    /**
     * Überprüft die Sessiondaten bei einer Geschützten Seite 
     * auf die Mindestrechte die ein User haben muss
     * um diese Seite zu betreten
     *
     * @param int $mindestrecht
     * @return 
     */
    function testrecht($mindestrecht)
    {
        // User Name oder SessionID nicht gesetzt?
        if(!$_SESSION[$this->cookie_prefix."UID"] || !$_SESSION[$this->cookie_prefix."SID"])
        {
            // Nicht eingeloggt / Bitte Einloggen
            return array(false, 0);
        }
        
        // Entsprechenden User aus der Datenbank holen
        $db = new MySQLConnection();        
        $query = 'SELECT *, (UNIX_TIMESTAMP()-UNIX_TIMESTAMP(`LastActivityTime`)) as `TimeDiff` FROM `'.$this->user_table.'` WHERE `UID`="'.mysql_escape_string($_SESSION[$this->cookie_prefix.'UID']).'" AND `SID`="'.mysql_escape_string($_SESSION[$this->cookie_prefix.'SID']).'";';
        $result=$db->db_sql($query);

        // Kein User gefunden?
        if($db->anz != 1)
        {
            // Nicht eingeloggt / Bitte Einloggen
            return array(false, 0);
        }

        // Zu Lange Inaktiv?
        if($result[0]['TimeDiff'] / 60 > $this->idle)
        {
            return array(false, 3);
        }

        // Kein Admin (Status == 1) und
        // Status nicht hoch genug 
        if($result[0]['Status'] != 1 && ($mindestrecht > $result[0]['Status']))
        {
            return array(false, 0);
        }
        // Zugriff Erlaubt
        return array(true, 4);
    }

    /**
     * Aktualisiert die MySQL Tabelle
     *
     */
    function schreibrecht()
    {
        // Gültige Session Daten?
        if($_SESSION[$this->cookie_prefix."UID"] && $_SESSION[$this->cookie_prefix."SID"])
        {
            // Datenbank Aktualisieren
            $db = new db1();
            $query = 'UPDATE `'.$this->user_table.'` SET `LastActivityTime`=NOW() WHERE `UID`="'.mysql_escape_string($_SESSION[$this->cookie_prefix.'UID']).'" AND `SID`="'.mysql_escape_string($_SESSION[$this->cookie_prefix.'SID']).'";';
            $result=$db->db_sql($query);
        }
    }

    /**
     * Loggt den Aktuellen User aus
     *
     */
    function logout()
    {
        // Datenbank aktualisieren
        $db = new MySQLConnection();
        $query = 'UPDATE `'.$this->user_table.'` SET `LastActivityTime`=0, `SID`="" WHERE `UID`="'.mysql_escape_string($_SESSION[$this->cookie_prefix.'UID']).'";';
        $result=$db->db_sql($query);
        // Session Daten löschen
        $_SESSION[$this->cookie_prefix.'SID'] = '';
        $_SESSION[$this->cookie_prefix.'UID'] = '';
    }

}
?>
Datenbank:
sql Code:
  1. CREATE TABLE `users` ( `UID` INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY ,
  2. `Username` VARCHAR( 25 ) NOT NULL ,
  3. `Password` VARCHAR( 32 ) NOT NULL ,
  4. `LastActivityTime` DATETIME NOT NULL ,
  5. `SID` VARCHAR( 32 ) NOT NULL ,
  6. `Status` INT UNSIGNED NOT NULL
  7. )



gruß Daniel
 
Hi für Sicher halt ich das nicht! Warum schreibst du das alles in eine Klasse.Das muß doch dann in jede Datei wieder eingebunden werden und verlängert unötig den Programm code und die Seite wird wieder mal einwenig langsamer.

So nun zur Sicherheit warum werden hier immer wieder die klassischen fehler gemacht und mit true oder False bei eine Pw kontrolle gearbeitet.Wie viele wissen gelingt es einen gültigen String zu einschleusehen der so in der art aussieht(' or 1=1#) dann wird kein Datensatz gefunden und die If abfrage gibt true zurück nun ist der User eingelogt.

PHP:
// User aus der DB holen
        $db = new MySQLConnection();
        $result=$db->db_sql('SELECT *, (UNIX_TIMESTAMP()-UNIX_TIMESTAMP(`LastActivityTime`)) as `TimeDiff`, FROM `"'.$this->user_table.'"` WHERE `Username`="'.mysql_escape_string($_POST['Username']).'";');
    
        // Existiert nur einer
        if($db->anz != 1 )
        {
            // Nicht eingeloggt / Ungültiger Benutzername 
            return array(false, 1);
        }
    
        // Stimmt das Kennwort überein?
        if(md5($_POST['Password']) != $result[0]['Password'])
        {
            // Nicht eingeloggt / Ungültiges Passwort
            return array(false, 2);
        }
    
        // Neue Session ID dem User Zuweisen

Unsicher ist if(md5($_POST['Password']) != $result[0]['Password'])
(ergebiss könnte lauten md5( " ") != (" " ) den if ist false und wird nicht ausgeführt. also gibt true zurück und er ist eingelogt
weiter ist unsicher:
'SELECT *, (UNIX_TIMESTAMP()-UNIX_TIMESTAMP(`LastActivityTime`)) as `TimeDiff`, FROM `"'.$this->user_table.'"` WHERE `Username`="'.mysql_escape_string($_POST['Username']).'";');
Direktes Post in einen Sql befehl einfügen brigt immer die Gefahr das der Sql string manipuliert werden kann auch wenn mysql_escape_string verwendet wurde.

var $logout_site = 'index.php?site=logout';
Wenn man mit mehren Verzeichnissen arbeitet kann es dann vorkommen das der Link nicht mehr gefunden wird.Man muß gegebensfall je nach Seite manuel angepasst werden.
 
Zuletzt bearbeitet:
Wie soll man trotz mysql_escape_string einen SQL Code einschleusen können? Gut ich könnte noch eine zusätzliche Abfrage einbauen welche nach dem der Userdaten nocheinmal in der Datenbank überprüft ob der User vorhanden ist. Zudem wird in testrecht() überprüft ob der User mit der User-ID $_SESSION[UID'] und der Session-ID $_SESSION['SID'] existiert. Wenn nicht wird er nicht eingeloggt.
 
Ich hab mich nochmals in die Literatur geworfen und weiter an den Abfragen gefeilt. Ich verwende nun mysql_real_escape_string und überprüfe das Format des Usernamens. Mehr Sicherheitsabfragen kenn ich nicht und ich wüsste was ich sonst noch ändern könnte:
PHP:
	function testlogin()
	{
		// Evt. Escaped Quotes entfernen
		$username = (get_magic_quotes_gpc()) ? stripslashes($_POST['Username']) : $_POST['Username'];
		$password = (get_magic_quotes_gpc()) ? stripslashes($_POST['Password']) : $_POST['Password'];
		
		// MySQL-Eigene Funktion für die Stringaufbereitung
		$username = mysql_real_escape_string($username);
		$password = mysql_real_escape_string($password);
		
		// Userformat checken ( a-z A-Z 0-9 , .- und mit keinem Zeichen oder Zahl beginnen)
		if(preg_match('/\\A[a-zA-Z]*[A-Za-z0-9._-]*\\z/', $username) ||
			strlen($username) > 20)
			{
				// Nicht eingeloggt / SQL Injection
				return array(false, 5);
			}
		
		// User aus der DB holen
		$db = new MySQLConnection();
		$userdata=$db->db_sql('SELECT * FROM `"'.$this->user_table.'"` WHERE `Username`="'.$username.'";');
	
		// Überprüfen ob es den User mit der gegebenen UID wirklich gibt
		$usercheck = $db->db_sql('SELECT *,(UNIX_TIMESTAMP()-UNIX_TIMESTAMP(`LastActivityTime`)) as `TimeDiff` FROM `'.$this->user_table.'` WHERE `UID`="'.$userdata[0]['UID'].'";');
				
		// Existiert nur einer
		if($db->anz != 1 )
		{
			// Nicht eingeloggt / Ungültiger Benutzername 
			return array(false, 1);
		}
	
		// Stimmt das Kennwort überein?
		if(md5($password) != $usercheck[0]['Password'])
		{
			// Nicht eingeloggt / Ungültiges Passwort
			return array(false, 2);
		}
	
		// Neue Session ID dem User Zuweisen
		$query = 'UPDATE `'.$this->user_table.'` SET `LastActivityTime`=NOW(), `SID`="'.session_id().'" WHERE `UID`="'.$usercheck[0]['UID'].'";';
		$db->db_sql($query);
		
		// Sessiondaten Schreiben		
		$_SESSION[$this->cookie_prefix.'UID'] = $usercheck[0]['UID'];
		$_SESSION[$this->cookie_prefix.'SID'] = session_id();
		return array(true, 4);
	}
 
Zurück