tutorials.de Buch-Aktion 05/2012
Like Tree1Danke
  • 1 Beitrag von Bratkartoffel
ERLEDIGT
NEIN
ANTWORTEN
3
ZUGRIFFE
1431
EMPFEHLEN
  • An Twitter übertragen
  • An Facebook übertragen
AUF DIESES THEMA
ANTWORTEN
  1. #1
    Orex Orex ist offline Mitglied
    Registriert seit
    Jul 2008
    Beiträge
    14
    Hi, ich bin was Sicherheit angeht ein absoluter Neuling.
    Habe die letzten Tage das erste mal versucht einen Login "absolut" Sicher zu Programmieren, da ich einen Login benötige der das maximum an Sicherheit bietet.



    So jetzt ein Post der eher für die härteren ist... Aber wenn dann richtig******



    Vorerst zur Struktur. Ich hoffe meine Projectstruktur ist leicht zuverstehen:

    ../
    aliases enthält die defines
    classes enthält die Klassen
    processes enthält die processe und ruft die KlassenFunktionen auf
    public_html Ordner der vom Webserver als "root" gesehen wird und im www erreichbar ist
    projectname
    css enthält die css datein
    view enthält die formulare bzw. das was zu sehen ist
    index.php ruft klassen auf und bindet die benötigten datein ein



    Alle Ordner bis auf die CSS Ordner enthalten IMMER 3 Datein.
    1. .htaccess (Order deny,allow Deny from all)
    2. index.php und index.html

    beide enthalten:

    HTML-Code:
    <html>
        <head>
            <title>ACCESS DENIED</title>
            <meta HTTP-EQUIV="REFRESH" content="0; url=http://www.xy.de/" />
        </head>
        <body></body>
    </html>
    Das war mein Versuch alles vor einem "oberflächlichem" Zugriff zu schützen. Somit ist kein Ordner aufrufbar und keine Datei die in dem Ordner enthalten ist.
    Darüber hinaus ist jede .php Datei nocheinmal am Anfang mit einem code geschützt, der es nicht erlaubt die .php alleine aufzurufen, sondern nur ein include über die index.php zulässt.

    Das Project enthält bis jetzt:
    1. Die Möglichkeit sich zu registrieren
    2. Die Möglichkeit sich einzuloggen
    3. Die Möglichkeit sich auszuloggen

    Das ist wie folgt realisiert:

    der Ordner VIEW welcher sich an der Oben genannten Stelle in /public_html/projectname/view befindet, enthält die Formulare.

    REGISTER

    PHP-Code:
    <?php
    defined
    '_UEBERINDEX' ) or die( header('location: http://xy.de/') );

    if(!isset(
    $_SESSION['usr_id'])){ ?>
    <form action="<?php echo $_SERVER['PHP_SELF']; ?>" id="registerForm"  method="POST" class="registerForm">
        <span class="registerForm">email</span>     <input type="text" name="reg_email" size="10" maxlength="25" class="registerForm" />
        <span class="registerForm">passwort</span>  <input type="password" name="reg_pass" size="10" maxlength="25" class="registerForm" />
        <input type="submit" name="formsubmit_registerForm" value="register" class="registerForm" />
    </form>
    <?php ?>

    LOGIN

    PHP-Code:
    <?php
    defined
    '_UEBERINDEX' ) or die( header('location: http://xy.de/') );

    if(!isset(
    $_SESSION['usr_id'])){ ?>
    <form action="<?php echo $_SERVER['PHP_SELF']; ?>" id="loginForm"  method="POST" class="loginForm">
        <span class="loginForm">email</span>       <input type="text" name="login_email" size="10" maxlength="25" class="loginForm" value="" />
        <span class="loginForm">passwort</span>    <input type="password" name="login_pass" size="10" maxlength="25" class="loginForm" value="" />
        <input type="submit" name="formsubmit_loginForm" value="login" class="loginForm" />
    </form>
    <?php ?>

    LOGOUT

    PHP-Code:
    <?php
    defined
    '_UEBERINDEX' ) or die( header('location: http://xy.de/') );

    if(isset(
    $_SESSION['usr_id'])){ ?>
    <form action="<?php $_SERVER['PHP_SELF']; ?>" id="registerForm" method="POST">
         <input type="hidden" name="logout" value="1">
         <input type="submit" name="formsubmit_logoutForm" value="logout" />
    </form>
    <?php ?>

    Diese Formulare rufen die Index.php auf über die sie included wurden.

    PHP-Code:
    <?php
    session_start
    ();

        
    define'_UEBERINDEX');
        
    //ALLE DATEN SHCON HIER AUFBEREITEN

        
    require_once    '../../classes/user.php';
        require_once    
    '../../classes/SQLconfig.php';
        require_once    
    '../../classes/SecurityHandler.php';
        require_once    
    '../../aliases/aliases.php';


        
    $user = new User;
        
    $security = new SecurityHandler();
        
    $dbconn = new OIConf();
        
    $dbconn->connect();

        require_once    
    '../../processes/package_user/package_user_processes.php';
        if(isset(
    $_SESSION['usr_id'])){$security->save_session();}




    ?>

    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
    <html>
        <head>
            <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
            <title></title>
            <?php include 'css/style.php'?>
        </head>
        <body>
            <span style="color:red;">
                <?php

        
    foreach($_SESSION as $k => $v){
            if(
    $k == 'error'){
                foreach(
    $v as $vk => $vv){
                echo 
    $vv.'<br>';}
            }
        

        }
    unset(
    $_SESSION['error']);


    ?></span>
            <div id="top"></div>
            <div id="left">
                <?php
            
    echo $security->isUserLoggedin();
            include 
    "view/package_user/module_logout/logoutForm.php";
            echo 
    '<br />';
            include 
    "view/package_user/module_register/registerForm.php";
            echo 
    '<br />';
            include 
    "view/package_user/module_login/loginForm.php";
      
            
    ?></div>
            <div id="middle"></div>
            <div id="right"></div>
        </body>
    </html>

    <?php $dbconn->disconnect();


    ?>

    Die index.php enthält wie oben gezeigt den Code "require_once '../../processes/package_user/package_user_processes.php';"
    Darin sind die .php Datein die die Anfrage der Formulare verarbeiten.

    die .php Datein liegen im Ordner /processes und sind nicht über das Webinterface aufrufbar.

    jedes Formular hat eine Datei die den Prozess verarbeitet und auch auf ungültige eingaben überprüft, sowie einen Fehler an den User ausgibt wenn etwas nicht stimmt.



    REGISTER


    PHP-Code:
    <?php
    defined
    '_UEBERINDEX' ) or die( header('location: http://xy.de/') );

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

        
    if(
    strlen($_POST['reg_pass'])< 6){
        
    $_SESSION['error']['001'] = SESSION_ERROR01;
        
    $confirmprocess false;
          
    }
    if(
    strlen($_POST['reg_pass'])> 20){
        
    $_SESSION['error']['002'] = SESSION_ERROR02;
        
    $confirmprocess false;
            }


    if(
    strlen($_POST['reg_email'])< 6){
        
    $_SESSION['error']['003'] = SESSION_ERROR03;
        
    $confirmprocess false;
         
    }

    if(
    strlen($_POST['reg_email']) > 50){
        
    $_SESSION['error']['004'] = SESSION_ERROR04;
        
    $confirmprocess false;
    }

                
               

                        
    if(!isset(
    $confirmprocess)){

                
    $checkMail  =   $security->stringControl($_POST['reg_email']);
                
    $checkPass  =   $security->stringControl($_POST['reg_pass']);
                
    $checkMail  =   $security->checkemail($checkMail);

    if(
    $checkMail != false && $checkPass != false && !isset($_SESSION['user_id'])){


                    
                        
    $ver_passwort $security->verschluesseln($checkPass);
                        
    $user->set_email($checkMail);
                        
    $user->quick_save_user($ver_passwort$security->getSalt());

                        echo 
    'regestriert! als '.$user->get_email().'';


    }

    elseif(!
    $checkMail){
        
    $_SESSION['error']['005'] = SESSION_ERROR05;
    }
    elseif(!
    $checkPass){
        
    $_SESSION['error']['006'] = SESSION_ERROR06;
    }
    elseif(isset(
    $_SESSION['user_id'])){
        
    $_SESSION['error']['007'] = SESSION_ERROR07;
    }
    else{
        
    $_SESSION['error']['000'] = SESSION_ERROR00;
    }

    }
    }
    ?>
    LOGIN

    PHP-Code:
    <?php
    defined
    '_UEBERINDEX' ) or die( header('location: http://xy.de/') );

    if(isset(
    $_POST['formsubmit_loginForm']) == 'login'){
        
                
    $checkPass  =   $security->stringControl($_POST['login_pass']);
                
    $checkMail  =   $security->stringControl($_POST['login_email']);
                            


    if(
    $checkMail != false && $checkPass != false){


                        
    $user->get_user($checkMail,$checkPass);
                        
                       

    }

    elseif(!
    $checkMail){
        
    $_SESSION['error']['001'] = SESSION_ERROR01

    }
    elseif(!
    $checkPass){
        
    $_SESSION['error']['002'] = SESSION_ERROR02

    }
    else{
        
    $_SESSION['error']['000'] = SESSION_ERROR00;

    }
    }


    ?>

    LOGOUT

    PHP-Code:
    <?php
    defined
    '_UEBERINDEX' ) or die( header('location: http://xy.de/') );


    if(
    $_POST['logout'] == 1){


            
    $_SESSION = array();
            if(
    $_COOKIE['session_name()']){
            
    setcookie(session_name(), ''time()-42000'/');
            }
            
    session_unset();
            
    session_destroy();
            
    header('location: /connectionplattform/index.php');
    }

    ?>

    Ist die Eingabe O.K. wird die jeweilige Funktion der Klasse aufgerufen, die dann den Eintrag in die Datenbank vornimmt oder Überprüft ob der User in der der Datenbank ist sowie das Passwort verschlüsselt usw.

    Dafür gibt es 3 Klassen:

    SQLconfig.php enthält die SQL config Datein.
    SecurityHandler.php verschlüsselt Passwörter Kontolliert eingaben usw.
    user.php alles was mit dem User zu tun hat (user speichern, DB abrufen, einloggen usw.)


    Aus Übersichtsgründen kommen die Klassen im nächsten Post....
    Geändert von Orex (26.11.10 um 02:16 Uhr)
     

  2. #2
    Orex Orex ist offline Mitglied
    Registriert seit
    Jul 2008
    Beiträge
    14
    Zu den Klassen. Es 3 Klassen:

    SQLconfig.php enthält die SQL config Datein.
    SecurityHandler.php verschlüsselt Passwörter Kontolliert eingaben usw.
    user.php alles was mit dem User zu tun hat (user speichern, DB abrufen, einloggen usw.)



    SQLconfig.php

    PHP-Code:
    <?php
    defined
    '_UEBERINDEX' ) or die( header('location: http://xy.de/') );

    /**
     * Description of SecurityHandler
     *
     * @author 1337
     */

    class SecurityHandler {
        private 
    $input;
        private 
    $output;
        private 
    $tryCount;
        private 
    $sessionHash;
        private 
    $salt;

        function 
    isUserLoggedin(){

            if(isset(
    $_SESSION['user_id'])){return true;}
            else{return 
    false;}
        }

        function 
    stringControl($input){
            
            
    $output mysql_real_escape_string($input);

            if(
    $input == $output){return $output;}
            else{return 
    false;}

            }

        function 
    erzeugeSalt() {


            
    $auswahl "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz123456789?}]{[%§&!?ß";
            
    $salt substrstr_shuffle$auswahl ), 0);
            
    $salt htmlentities($salt);
            
    $this->salt $salt;
            return 
    $salt;
        }


        function 
    verschluesseln($pass$salt=NULL) {
            if(
    $salt == NULL)
              
    $salt $this->erzeugeSalt();
            
    $cryptedPasswort hash('xxx'.$pass.$salt.'xxx').'xxx'//xxx sind fixed salts

            
    return $cryptedPasswort;
        }  

        function 
    getSalt(){
            return 
    $this->salt;
        }

        function 
    stringControl_id($id){

            
    $id = (int)$id;
            return 
    $id;
        }

    function 
    checkemail($email) {

    // externes script! Danke an den Autor "KD3" , hat mir etwas schreibarbeit erspart!!
    // http://www.tutorials.de/php-codeschnipsel/303451-email-adresse-ueberpruefen.html

      
    $email htmlspecialchars($email);    // Email-Adresse ( z.B someone@gmx.de

      
    $r false;                           // Standard auf false gesetzt, weil wenn die
                                            // if-Abfragen abweichend sind und kein true zurückgegeben wird, dann
                                         

          
    if(preg_match('/(.*?)\@(.*?)\.(\w){2,6}/i'$email)) {

              
    $split explode('@'$email);     // [0] => someone , [1] => gmx.de
              
    $split2 explode('.'$split[1]); // [0] => gmx , [1] => de

                
    if(preg_match('/([a-z]){3,64}/i'$split2[0])) {

                    if(
    fsockopen($split[1], 80)) {

                        if(
    preg_match('/([a-z0-9\!\"\$\&\/\(\)\?\~\#\+\.\:\_\-]+){1,64}[^\@]/i'$split[0])) {

                            
    $r true;

                            }
                        }
                    }
              }
              if(
    $r == true){return $email;}
              else{return 
    false;}

              
    }

    function 
    refreshSession(){
            
    $_SESSION = array();
            if(
    $_COOKIE['session_name()']){
            
    setcookie(session_name(), ''time()-42000'/');
            }
            
    session_unset();
            
    session_destroy();
            
    header("location: /index.php");
    }

    function 
    save_session(){
        
    $sessionid session_id();
        
    $client_ip $_SERVER['REMOTE_ADDR'];
        
    $usr_id = (int)$_SESSION['usr_id'];
        
        
    $query  "INSERT INTO sec (usr_id,sec_usr_session,sec_usr_ip) Values('".$usr_id."','".$sessionid."','".$client_ip."')";
        
    $result mysql_query($query)
        or die(
    mysql_error());

        return 
    $result;
    }

    }
    ?>


    SQLconfig.php

    PHP-Code:
    <?php
    defined
    '_UEBERINDEX' ) or die( header('location: http://xy.de/') );


    class 
    OIConf{
        private 
    $databaseURL "xxxx";
        private 
    $databaseUName "xxxx";
        private 
    $databasePWord "xxxx";
        private 
    $databaseName "xxxx";
        private 
    $connection;

            
        function 
    connect(){
            
    $this->connection mysql_connect($this->databaseURL,$this->databaseUName,$this->databasePWord);     
            
    $db mysql_select_db($this->databaseName$this->connection);
        }

        function 
    disconnect(){
            
    mysql_close($this->connection);
        }
    }
    ?>


    user.php

    PHP-Code:
    <?php
    defined
    '_UEBERINDEX' ) or die( header('location: http://xy.de/') );
    /*
    This is a Guest helper class. Instance of
    this class can store Guest information.
    */

    class User{
        
        private 
    $UID;
        private 
    $name;
        private 
    $email;
        
    //private $firstName;
        //private $lastName;
        //private $country;
        //private $streetNr;
        //private $plz;
        //private $city;
        //private $iban;
        //private $swift;
        //private $birthday;
        /* ... */
        
    private $adress;
        private 
    $balance;
        private 
    $puk;

        
       

        function 
    set_email($email){
                
    $this->email $email;
            }
        function 
    set_name($name){
                
    $this->name $name;
            }
        function 
    set_balance($balance){
                
    $this->balance $balance;
            }
        function 
    set_adress($adress){
                
    $this->adress $adress;
            }
            
        function 
    get_UID(){
                return 
    $this->UID;
            }
        function 
    get_name(){
                return 
    $this->name;
            }
        function 
    get_email(){
                return 
    $this->email;
            }
        function 
    get_balance(){
                return 
    $this->balance;
            }
        function 
    get_adress(){
                return 
    $this->adress;
            }

            

        function 
    quick_save_user($password,$salt){

            
    $query  "INSERT INTO users (usr_pass,usr_email,usr_puk) Values('".$password."','".$this->get_email()."','".$salt."')";
            
    $result mysql_query($query)
            or die(
    mysql_error());

            return 
    $result;

        }

        function 
    change_user(){

            
    $query  "UPDATE users SET usr_email = '" $this->get_email() ."' WHERE user_id = '"$this->get_UID() ."'";
            
    $result mysql_query($query)
            or die(
    mysql_error());

            return 
    $result;

        }

        function 
    get_user($email$passwort){

           
            
    $query      " SELECT usr_puk
                            FROM users
                            WHERE usr_email = '
    $email' ";
            
    $result     mysql_query($query)
            or die(
    mysql_error());
            
    $row        mysql_fetch_array($result);


            
    $passwort   SecurityHandler::verschluesseln_2($passwort,$row["usr_puk"]);

            
    $query      " SELECT *
                            FROM users
                            WHERE usr_pass = '"
    .$passwort."'";
            
    $result     mysql_query($query)
            or die(
    mysql_error());
            
            
    $row mysql_fetch_assoc($result);


            if(!
    $row){
                
    $_SESSION['error']['008'] = SESSION_ERROR08;
     
            }
            else{

            
    $_SESSION['usr_email']  = $row["usr_email"];
            
    $_SESSION['usr_id']     = $row["usr_id"];
            
            }

            return 
    true;

        }


    }
    ?>



    Eine mir sehr wichtige Frage wäre noch. Ich würde die Userdaten, also EMAIL, USER_ID, NAME, ADRESSE, usw (passwort natürlich nicht) in der $_SESSION variable speichern, da ich es ständig brauche und nicht jedes mal die Datenbank belästigen will.... Ist das sicher?
    Falls es zu einem SessionHijacking kommen würde, wäre es eh wurscht weil der Angreifer dann die Daten auch hätte, wenn er die Seiten aufrufen würde. Ändern könnte er nichts, da ich davor nochmal ein Passwort check machen würden..









    So abschließend:

    JEDEM der das ganze überhaupt liest schonmal ein RIESENGROßES DANKE!

    Sollte jemandem dann etwas auffallen was er für eine Sicherheitslücke hält, wäre ich ihm sehr Dankbar wenn er das einfach schnell drunter Posten würde!
    Sollte das Script ausgezeichnet sein und keine mängel enthalten, freu ich mich natürlich auch über ein positives feedback.


    Vielen Dank
    und eine schöne restliche Nacht
    Geändert von Orex (26.11.10 um 16:20 Uhr) Grund: fixed function verschlüsseln - thx 2 Bratkartoffel
     

  3. #3
    Avatar von Bratkartoffel
    Bratkartoffel Bratkartoffel ist offline gebratene Kartoffel
    tutorials.de Premium-User
    Registriert seit
    Jun 2007
    Ort
    Passau (Niederbayern)
    Beiträge
    1.394
    Hi,

    habe mir jetzt noch nicht alles durchgelesen (dauert ja auch ein bisschen ), mir ist aber schon was aufgefallen:

    In der SecurityHandler die Funktion erzeugeSalt()
    Diese erzeugt bei jedem Aufruf einen anderen Salt, den du dann an das Passwort anhängst. Dieser Salt geht aber verloren oder? Wie soll sich dann der Benutzer nochmals anmelden, bzw. wie soll dann das Passwort überprüft werden? Dynamische Salts müssen von irgendwas, das mit dem Benutzer assoziiert ist zusammenhängen (z.B.: email-adresse, benutzername, vorname etc.)

    Ausserdem könntest du das auch so verkürzen:
    PHP-Code:
    function verschluesseln($pass$salt=NULL) {
            if(
    $salt == NULL)
              
    $salt $this->erzeugeSalt();
            
    $cryptedPasswort hash('xxx'.$pass.$salt.'xxx').'xxx'//xxx sind fixed salts

            
    return $cryptedPasswort;
        } 
    Somit sparst du dir die verschluesseln_2().

    In der erzeuge_salt() kannst du das htmlentities() rausnehmen, du hast ja nur ASCII-Zeichen:
    PHP-Code:
    function erzeugeSalt() {
            
    $auswahl "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz123456789?}]{[%§&!?ß";
            
    $salt substrstr_shuffle$auswahl ), 0);
            
    $this->salt $salt;
            return 
    $salt;
        } 
    In der checkemail() prüfst du auch, ob der Mailserver einen Webserver hat. Diese überprüfung ("if(fsockopen($split[1], 80)) { ") würde ich rausnehmen, ich kenne ein paar Mailserver, die keinen Webserver auf Port 80 laufen haben und somit würden die Adresse abgelehnt werden. Würde hier eher auf Port 25 die Verbindung öffnen (SMTP), der müsste bei jedem Mailserver offen sein.

    $_SESSION sollte sicher sein, die Daten können vom Benutzer nicht direkt manipuliert werden da diese nur auf dem Server bleiben.

    Dass du jede Änderung an dem User sofort in die Datenbank schreibst finde ich etwas unperformant. Ich habe da normalerweise so einen Ansatz:
    PHP-Code:
    class Test {
      private 
    $id;
      private 
    $name;
      
    // weitere felder

      
    private $is_dirty false;

      public function 
    __construct($pk_in_db) {
        
    // von db laden
      
    }

      public function 
    __destruct() {
        if(
    $this->is_dirty$this->saveToDb();
      }

      private function 
    saveToDb() {
        
    // in die db speichern
         
    $this->dirty false;
      }

      
    // Hier die ganzen set-Methoden, sobald ein Wert verändert wird,
      // dann wird die $this->dirty auf true gesetzt.

    Du kannst im obigen Beispiel also die set-Methoden beliebig oft aufrufen, die Daten werden dann nur einmal in die Datenbank gespeichert (wenn die Seite fertig ist, also der Destruktor aufgerufen wird). Bei dir könnte es halt sein dass du die save-Methode vergisst aufzurufen, bei obigen funktioniert das automatisch

    Gruß
    BK
    Geändert von Bratkartoffel (26.11.10 um 14:05 Uhr)
    Orex bedankt sich. 
    Über eine gute Bewertung freut sich jeder ;)
    Bitte erledigte Threads als "Erledigt" markieren.

    "Though a program be but three lines long, someday it will have to be maintained.''
    -- Geoffrey James, "The Tao of Programming"

  4. #4
    Orex Orex ist offline Mitglied
    Registriert seit
    Jul 2008
    Beiträge
    14
    Hey, vielen dank schonmal! Werde deine Vorschläge sofort umsetzten und nacher auch die Posts mal ändern!

    Der Salt wird in der DB gespeichert. Jeder user hat nen anderen Salt und der wird in der spalte "tan" abgelegt.
    Geändert von Orex (26.11.10 um 15:03 Uhr)
     

Ähnliche Themen

  1. Sicherer Auto-Login
    Von ZodiacXP im Forum Coders Talk
    Antworten: 0
    Letzter Beitrag: 04.09.09, 11:30
  2. Sicherer Upload
    Von Pius Hermit im Forum PHP
    Antworten: 5
    Letzter Beitrag: 11.01.09, 21:09
  3. Sicherer Hash
    Von ZodiacXP im Forum PHP
    Antworten: 3
    Letzter Beitrag: 05.09.08, 00:13
  4. 2x Escapen sicherer?
    Von KD3 im Forum PHP
    Antworten: 1
    Letzter Beitrag: 22.05.07, 01:10
  5. Sicherer Login mit CMS
    Von Lyn555 im Forum Coders Talk
    Antworten: 3
    Letzter Beitrag: 17.03.07, 13:56

Stichworte