+ Antworten
Seite 1 von 7 1 2 3 4 ... LetzteLetzte
Ergebnis 1 bis 15 von 99

Thema: [Tutorial] Login Systeme von Einfach bis Profi

  1. #1
    TP-Specialist phpBuddy ist einer der Eckpfeiler des TP - ohne ihn geht nichts phpBuddy ist einer der Eckpfeiler des TP - ohne ihn geht nichts phpBuddy ist einer der Eckpfeiler des TP - ohne ihn geht nichts phpBuddy ist einer der Eckpfeiler des TP - ohne ihn geht nichts phpBuddy ist einer der Eckpfeiler des TP - ohne ihn geht nichts phpBuddy ist einer der Eckpfeiler des TP - ohne ihn geht nichts phpBuddy ist einer der Eckpfeiler des TP - ohne ihn geht nichts phpBuddy ist einer der Eckpfeiler des TP - ohne ihn geht nichts phpBuddy ist einer der Eckpfeiler des TP - ohne ihn geht nichts phpBuddy ist einer der Eckpfeiler des TP - ohne ihn geht nichts phpBuddy ist einer der Eckpfeiler des TP - ohne ihn geht nichts Avatar von phpBuddy
    Registriert seit
    Aug 2004
    Ort
    Kaiserslautern
    Beiträge
    4.678

    [Tutorial] Login Systeme von Einfach bis Profi

    Vorwort

    Relativ häufig wird nach Möglichkeiten gefragt, wie man Uploads, einzelne Seiten oder ganze Bereiche einer Webseite mit einem Passwortschutz versehen kann. Dabei können die Anforderungen an so einen Passwortschutz ganz unterschiedlich ausfallen. In manchen Fällen reicht es aus, das nur ein Benutzer mit einem Passwort Zugriff erhält, während es in anderen Fällen nötig ist mehrere Benutzer, unabhängig voneinander, zu verwalten und Zugriff zu gestatten. Um verschiedene Anforderungen und Bereiche abzudecken, möchte ich Euch mit diesem Tutorial 3 Techniken etwas näher bringen, die Ihr als Basis für ein eigenes Login System verwenden könnt. Hauptbestandteil dieser Techniken ist das Session Management System von PHP. Aufgeteilt ist dieses Tutorial in:
    1. Einfach - Ein simples Login System mit 1 Benutzer und 1 Passwort. Ausreichend um Uploads oder einzelne Seiten vor Zugriffe Unbefugter zu schützen. Kommt mit wenigen Zeilen Code und ohne Datenbank aus.
    2. Fortgeschritten - Mit diesem System kann man beliebig viele Benutzer verwalten, da wir mit einer MySQL Datenbank arbeiten. Neben dem einfachen Login setzen wir auch Cookies ein, damit sich die Seite an den Benutzer erinnert.
    3. Profi - Auch hier arbeiten wir mit einer MySQL Datenbank, wodurch beliebig viele Benutzer verwaltet werden können. Schwerpunkt bei diesem System liegt aber ganz klar auf dem Punkt Sicherheit. Es werden Schutzmaßnahmen gegen Session Fixation und Session Hijacking getroffen; der Benutzer wird bei jedem Seitenaufruf neu validiert; Hackversuche (Brute Force) werden erkannt und Benutzerkonten automatisch deaktiviert, bevor sie geknackt wurden; Passwörter werden durch die Zugabe von "Salts" sicherer gemacht; Benutzer werden bei längerer Inaktivität automatisch ausgeloggt, damit kein Unbefugter die Session übernehmen (hijacken) kann.



    Voraussetzungen
    • PHP 5.2.x oder höher
    • MySQL 5.x oder höher
    • PHP/MySQL Kenntnisse je nach Login System von Grundlagenwissen bis semi-professionell
    • ... und natürlich Lernbereitschaft

    Da der Quelltext weitestgehend kommentiert ist und entsprechende PHP Kenntnisse vorausgesetzt werden, werde ich die Erklärungen zu den Listings kürzer halten und nur wichtige Stellen etwas näher erläutern.
    Übrigens, für alle 3 Varianten lauten die Demo Zugangsdaten: Otto // geheim



    Variante Einfach

    Um "mal eben" eine Seite zu schützen oder für ein Datei Upload ein Passwort abzufragen, bietet sich diese Variante an. Die Zugangsdaten, sowie alle relevanten Funktionen für die Anmeldung sind direkt in die Login Datei geschrieben. Listing der Datei login_einfach.php:

    PHP-Code:
    <?php

    // Zugangsdaten
    $benutzername 'Otto';
    $passwort     'geheim';

    // Session starten
    session_start();

    // Variablen deklarieren
    $_SESSION['angemeldet'] = false;
    $fehlermeldung          '';

    // Wurde das Formular abgeschickt?
    if (isset( $_POST['login'] ))
    {
        
    // Maskierende Slashes aus POST Array entfernen
        
    if (get_magic_quotes_gpc())
        {
            
    $_POST array_map'stripslashes'$_POST );
        }
        
    // Benutzereingabe mit Zugangsdaten vergleichen
        
    if (strtolower$benutzername ) == strtolowertrim$_POST['benutzer'] )) &&
            
    $passwort == trim$_POST['passwort'] ))
        {
            
    // Wenn die Anmeldung korrekt war Session Variable setzen
            // und auf die geheime Seite weiterleiten
            
    $_SESSION['angemeldet'] = true;
            
    header'location: geheim_einfach.php' );
            exit;
        }
        else
        {
            
    // Wenn die Anmeldung fehlerhaft war, Fehlermeldung setzen
            
    $fehlermeldung '<h3>Die Anmeldung war fehlerhaft!</h3>';
        }
    }

    ?>
    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="de" lang="de">
    <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <title>phpBuddy.eu - Login Script</title>
    </head>

    <body>

    <?php
    // Falls die Fehlermeldung gesetzt ist
    if ($fehlermeldung) echo $fehlermeldung;
    ?>

    <form id="loginform" method="post" action="<?php echo $_SERVER['PHP_SELF']; ?>">
        <label for="benutzer">Benutzer: </label><input type="text" name="benutzer" id="benutzer" value="" /><br />
        <label for="passwort">Passwort: </label><input type="password" name="passwort" id="passwort" value="" /><br />
        <input type="submit" name="login" id="login" value="Anmelden" />
    </form>

    </body>
    </html>
    Die Funktionsweise ist einfach. Oben im Script legen wir Benutzername und Passwort fest, starten die Session, bereiten einige Variablen vor und sind für die Anmeldung gerüstet. Wird das HTML Formular abgeschickt, entfernen wir erst mal störende Maskierungen, die manchmal durch den Server hinzugefügt werden. Eine simple Kontrolle des eingegebenen Benutzername und Passwort mit den festgelegten Werten teilt uns mit, ob der Zugriff berechtigt ist oder nicht. Dazu konvertieren wir den Benutzername in Kleinbuchstaben, weil manche User faul sind und sich hartnäckig weigern die Shift-Taste zu benutzen. Beim Passwort wäre das natürlich fatal, da hier eine Mischung aus Groß-/Kleinbuchstaben sogar gewünscht ist. Erweisen sich die Zugangsdaten als korrekt, setzen wir die Session Variable $_SESSION['angemeldet'] auf den Wert true und wissen somit, dass sich der Benutzer korrekt angemeldet hat. Über die header() Anweisungen leiten wir den Benutzer auf die geheime (geschützte) Seite. Waren die eingegebenen Daten fehlerhaft, weisen wir der Variable $fehlermeldung die entsprechende Meldung zu, wodurch sich deren Existenz auf wahr ändert. Im HTML Teil fragen wir den Status dieser Variable ab und ist der Wert wahr, was er ja durch die Fehlermeldung ist, geben wir die Meldung an den Benutzer aus.

    Nachdem der Benutzer, nach korrekter Anmeldung, auf die geschützte Seite geleitet wurde, prüfen wir hier natürlich auch noch einmal, ob sich der Benutzer zurecht hier aufhält. Es könnte ja auch sein, dass er nur den Name der Datei weiß und die Seite direkt im Browser aufgerufen hat. Hier das Listing der Datei geheim_einfach.php:

    PHP-Code:
    <?php

    // Session starten
    session_start();
    // Prüfen ob der Benutzer angemeldet ist
    if (!$_SESSION['angemeldet'])
    {
        
    // Zum Login umleiten
        
    header'location: login_einfach.php' );
        exit;
    }

    ?>
    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="de" lang="de">
    <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <title>phpBuddy.eu - Geheime Seite</title>
    </head>

    <body>

    <h3>Willkommen im geschützten Bereich! ;-)</h3>

    </body>
    </html>
    Uns interessiert nur der obere Bereich. Als erstes starten wir wieder die Session und prüfen, ob $_SESSION['angemeldet'] den Wert true hat. Ist das nicht der Fall, kam der Besucher nicht über die Login Seite und genau dorthin schicken wir ihn mit der header() Anweisung.

    Wichtig: Wenn man in PHP mit Sessions arbeitet, muß man in jeder Datei das session_start() im oberen Teil einfügen. Unterlässt man es die Session zu starten, gehen die Daten aus der Session verloren!

    Das war's schon! Mit so wenigen Zeilen Code kann man ein Mini-Login System verwirklichen. Wie mehrfach erwähnt, eignet sich diese Variante aber lediglich um eher unwichtige Bereiche/Seiten zu schützen. Wer wirklich sensible Inhalte schützen möchte, sollte sich Variante Profi anschauen.

    Wer mehrere Benutzer hat, die individuelle Zugangsdaten haben sollen, für den bietet sich die folgende Variante an.



    Variante Fortgeschritten

    Diese Variante macht Gebrauch von einer MySQL Datenbank und speichert den Anmeldestatus in einem Cookie. Schauen wir uns zunächst einmal das Listing von login_fortgeschritten.php an:

    PHP-Code:
    <?php

    // Session starten
    session_start();

    // Variablen deklarieren
    $_SESSION['angemeldet'] = false;
    $benutzername           '';
    $passwort               '';
    $fehlermeldung          '';

    // Funktion zum verbinden zur Datenbank
    function db_connect()
    {
        
    // Zugangsdaten für die DB
        
    $dbhost 'localhost';
        
    $dbuser 'root';
        
    $dbpass '';
        
    $dbname 'testlogin';

        
    // Verbindung herstellen und Verbindungskennung zurückgeben
        
    $conid mysql_connect$dbhost$dbuser$dbpass ) or die( 'Verbindungsfehler!' );
        if (
    is_resource$conid ))
        {
            
    mysql_select_db$dbname$conid ) or die( 'Datenbankfehler!' );
        }
        return 
    $conid;
    }

    // Prüfen ob ein Cookie existiert und zu einem gültigen User gehört 
    if (isset( $_COOKIE['UserLogin'] ))
    {
        
    // Wert aus dem Cookie mit dem Wert in der Datenbank vergleichen
        
    $conid db_connect();
        
    $sql "SELECT
                    `id`
                FROM
                    `login_fortgeschritten`
                WHERE
                    `cookie_hash` = '" 
    .mysql_real_escape_string$_COOKIE['UserLogin'] ). "' AND
                    `aktiviert` = 1"
    ;

        
    $ergebnis mysql_query$sql$conid );

        
    // Stimmt der Cookie Hash überein, wurde 1 Datensatz gefunden
        
    if (mysql_num_rows($ergebnis) == 1)
        {
            
    // Wenn der Hash aus dem Cookie mit dem aus der DB übereinstimmt,
            // Session Variable setzen und auf die geheime Seite weiterleiten
            
    $_SESSION['angemeldet'] = true;
            
    header'location: geheim_fortgeschritten.php' );
            exit;
        }
    }

    // Wenn das Formular abgeschickt wurde
    if (isset( $_POST['login'] ))
    {
        
    // Maskierende Slashes aus POST Array entfernen
        
    if (get_magic_quotes_gpc())
        {
            
    $_POST array_map'stripslashes'$_POST );
        }
        
        
    // Benutzereingabe umladen, von Leerzeichen befreien und 
        
    $benutzername strtolowertrim$_POST['benutzer'] ) );
        
    $passwort     md5trim$_POST['passwort'] ) );
            
        
    // Benutzereingabe mit User in der Datenbank vergleichen
        
    $conid db_connect();
        
    $sql "SELECT
                    `cookie_hash`
                FROM
                    `login_fortgeschritten`
                WHERE
                    LOWER(`benutzername`) = '" 
    .mysql_real_escape_string$benutzername ). "' AND
                    `passwort` = '" 
    .mysql_real_escape_string$passwort ). "' AND
                    `aktiviert` = 1"
    ;
        
        
    $ergebnis mysql_query$sql$conid );
        
        
    // Stimmen die Benutzereingaben überein, wurde 1 Datensatz gefunden
        
    if (mysql_num_rows($ergebnis) == 1)
        {
            
    // Abfrageergebnis fetchen
            
    $usercookie mysql_fetch_assoc$ergebnis );

            
    // Wenn die Anmeldung korrekt war Session Variable setzen,
            // COOKIE an Browser schicken und auf die geheime Seite weiterleiten
            
    $_SESSION['angemeldet'] = true;
            
    setcookie'UserLogin'$usercookie['cookie_hash'], time()+600 );
            
    header'location: geheim_fortgeschritten.php' );
            exit;
        }
        else
        {
            
    $fehlermeldung '<h3>Die Anmeldung war fehlerhaft!</h3>';
        }
    }

    ?>
    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="de" lang="de">
    <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <title>phpBuddy.eu - Login Script</title>
    </head>

    <body>

    <?php
    // Falls die Fehlermeldung gesetzt ist
    if ($fehlermeldung) echo $fehlermeldung;
    ?>

    <form id="loginform" method="post" action="<?php echo $_SERVER['PHP_SELF']; ?>">
        <label for="benutzer">Benutzer: </label><input type="text" name="benutzer" id="benutzer" value="" /><br />
        <label for="passwort">Passwort: </label><input type="password" name="passwort" id="passwort" value="" /><br />
        <input type="submit" name="login" id="login" value="Anmelden" />
    </form>

    </body>
    </html>
    Der HTML Teil dieses Scripts ist identisch mit dem vorherigen Beispiel. Ebenfalls starten wir auch hier als erstes eine Session und bereiten einige Variablen vor, die wir im weiteren Verlauf benötigen. Danach folgt eine Funktion mit dem Namen db_connect(), die für uns die Kommunikation mit der DB übernimmt. Aus der Funktion wird das Resource Handle zurückgeliefert, mit dem wir weiterarbeiten können. Wer nicht versteht was in dieser Funktion vorsich geht, ist falsch in diesem Tutorial und sollte zunächst einmal einen PHP/MySQL Crashkurs durcharbeiten!

    Mit der Zeile
    PHP-Code:
    if (isset( $_COOKIE['UserLogin'] )) 
    wird geprüft, ob ein Cookie existiert, das auf den Name UserLogin hört. $_COOKIE gehört, wie u.a. auch $_GET oder $_POST, ebenfalls zu den Superglobalen Arrays, die einen globalen Sichtbarkeitsbereich haben. Man kann also auch direkt aus einer Funktion darauf zugreifen. Der Name des Cookies stellt auch zugleich den Schlüssel des assoziativen Arrays dar. Wir können also mit $_COOKIE['UserLogin'] auf den Inhalt des Cookies zugreifen. Genau das machen wir auch im SQL Statement, indem wir einen Datensatz in der DB suchen, dessen Feld cookie_hash mit dem Wert im Cookie übereinstimmt. Finden wir einen Datensatz, handelt es sich offenbar um einen uns bekannten User. In diesem Fall setzen wir unsere Variable $_SESSION['angemeldet'] auf true und leiten den User auf unsere geheime Seite.

    Wurde kein Cookie gefunden, wird die Seite mit der Loginform angezeigt. Nach Abschicken des Formulars bereiten wir die Benutzereingaben auf, erzeugen einen md5-Hash vom eingegebenen Passwort und benutzen dieses, zusammen mit dem Benutzername, um unser SQL Statement zu formulieren. Wir lesen hier das Feld cookie_hash aus -das beim Anlegen des Benutzerkontos erzeugt werden muß und einmalig sein sollte! (ein md5-Hash der Registrierzeit würde sich anbieten)- das zum Datensatz gehört, bei dem die eingegebenen Zugangsdaten übereinstimmen. Haben wir einen Treffer, sind die Zugangsdaten korrekt und wir können den Benutzer einloggen.
    Wir setzen also die Variable $_SESSION['angemeldet'] wieder auf true, schicken einen Cookie zum Benutzer, damit wir ihn später wiedererkennen und leiten den Benutzer auf die geheime Seite weiter.

    Noch einmal zum setzen des Cookies. Der ganze Zauber findet in dieser Zeile statt:

    PHP-Code:
    setcookie'UserLogin'$usercookie['cookie_hash'], time()+600 ); 
    Mit setcookie() senden wir einen Cookie an den Browser des Benutzers. Ob ein Benutzer den Keks annehmen möchte oder nicht, liegt allein beim Benutzer. Es gibt keine Möglichkeit dem Benutzer einen Keks aufzuzwingen. Ebenso kann man nicht sofort prüfen ob der Keks beim Benutzer angekommen ist, sondern muß erst irgend eine Art von Reload auslösen. In unserem Fall geschieht das durch den header()-Redirect.
    Die Funktion setcookie() erwartet einige Parameter:
    • $name - Der Name des Cookie, über den wir den Inhalt auslesen können ($_COOKIE[$name])
    • $wert - Der Inhalt des Cookie. In der Regel Informationen die wir dort selbst abgelegt haben
    • $verfall - Ein Zeitstempel als INT Wert, bis wann der Keks gültig ist. (z.b. time()+60*60*24*30 wenn der Keks 30 Tage lang gültig sein soll)
    • $pfad - Damit kann man festlegen für welches Verzeichnis (mit Unterverzeichnisse) der Keks gültig sein soll. Wird kein Pfad angegeben, wird standardmäßig das Verzeichnis genommen in dem der Keks abgesetzt wurde.
    • $domain - Damit kann man den Keks allgemeingültig machen oder auf einzelne Sub-Domains beschränken
    • $sicher - Legt fest ob der Keks nur gültig ist, wenn er über eine HTTPS Leitung gesendet wurde. Der Wert von $sicher kann true oder false sein.
    • $nurHTTP - true oder false und legt fest, ob der Keks nur akzeptiert wird, wenn er über das HTTP Protokoll geschickt wurde. Es ist also nicht möglich den Keks durch Scripts o.ä. zu schicken. Dieser Wert sollte eigentlich immer auf true gesetzt werden, da er XSS Angriffe ungemein erschwert und somit wesentlich zur Sicherheit beiträgt. Hinweis: diese Option wurde erst mit PHP 5.2.0 eingeführt!

    Damit sollte das Login Script soweit klar sein. Kommen wir nun zur geheimen Seite; hier das Listing:

    PHP-Code:
    <?php

    // Session starten
    session_start();

    // Prüfen ob der Benutzer angemeldet ist
    if (!$_SESSION['angemeldet'])
    {
        
    // Zum Login umleiten
        
    header'location: login_fortgeschritten.php' );
        exit;
    }

    // Abmelden, Cookie löschen und zum Login umleiten
    if ($_GET['keks'] == 'loeschen')
    {
        
    setcookie'UserLogin'''time()-3600 );
        
    session_destroy();
        
    header'location: login_fortgeschritten.php' );
        exit;
    }

    ?>
    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="de" lang="de">
    <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <title>phpBuddy.eu - Geheime Seite</title>
    </head>

    <body>

    <h3>Willkommen im geschützten Bereich! ;-)</h3>

    <p><a href="<?php echo $_SERVER['PHP_SELF']; ?>?keks=loeschen">Abmelden und Cookie löschen</a></p>

    </body>
    </html>
    Der HTML Teil ist wieder unspektakulär. Im Vergleich zu vorher ist hier nur ein Link eingefügt, mit dem sich ein Benutzer abmelden kann.
    Im oberen Teil wieder das übliche Spiel: Session starten!
    Anschließend prüfen wir ob die Variable $_SESSION['angemeldet'] gesetzt ist. Falls nicht, leiten wir den Benutzer zur Login Seite. Der aufmerksame Leser wird jetzt feststellen: "Aber wieso haben wir einen Keks gesetzt, wenn der User die Seite hier nicht direkt aufrufen kann?". Das ist korrekt, da der Wert in der Session noch nicht gesetzt wurde, wird der Benutzer zum Login umgeleitet. Auf der Login Seite wird aber als erstes geprüft ob ein Cookie existiert und leitet ggfs. wieder zur geheimen Seite weiter. Es ist also quasi nur eine doppelte Umleitung.

    Als letztes im Script wird noch geprüft ob die Variable $_GET['keks'] den Wert loeschen hat und falls dem so ist, löschen wir den Keks, zerstören die Session und leiten den Benutzer zur Login Seite weiter.
    Um ein Cookie zu löschen muß es von der gleichen Domain gesendet werden, den selben Namen haben und die Zeit bis zum Verfall muß in der Vergangenheit liegen. Dann, und nur dann, weiß der Browser, dass der Keks gelöscht werden soll.


    Bevor wir uns an die Variante Profi machen möchte ich noch erwähnen, dass die Anmeldung bei Login Scripts, sowie die Kommunikation zwischen Server und Client bei sensiblen Daten stets über eine sichere HTTPS Leitung geschehen sollte, da eine normale, unverschlüsselte Leitung nicht sicher ist. Speziell dieser Punkt ist bei einfachen Webpaketen nicht immer möglich, da extra eine SSL Lizenz für eine verschlüsselte Leitung gekauft werden müsste, die oftmals mehrere Hundert Euro pro Jahr kostet. Manche Provider bieten aber, bei höherwertigen Paketen, einen SSL Proxy an. Dann ist man zwar nur über eine allgemeine Domain (meist nach dem Schema https://ssl.provider.de/meine-domain/) erreichbar, aber dafür ist es wenigstens sicher!
    Angehängte Dateien
    Geändert von phpBuddy (30.07.2008 um 00:16 Uhr)

  2. #2
    TP-Specialist phpBuddy ist einer der Eckpfeiler des TP - ohne ihn geht nichts phpBuddy ist einer der Eckpfeiler des TP - ohne ihn geht nichts phpBuddy ist einer der Eckpfeiler des TP - ohne ihn geht nichts phpBuddy ist einer der Eckpfeiler des TP - ohne ihn geht nichts phpBuddy ist einer der Eckpfeiler des TP - ohne ihn geht nichts phpBuddy ist einer der Eckpfeiler des TP - ohne ihn geht nichts phpBuddy ist einer der Eckpfeiler des TP - ohne ihn geht nichts phpBuddy ist einer der Eckpfeiler des TP - ohne ihn geht nichts phpBuddy ist einer der Eckpfeiler des TP - ohne ihn geht nichts phpBuddy ist einer der Eckpfeiler des TP - ohne ihn geht nichts phpBuddy ist einer der Eckpfeiler des TP - ohne ihn geht nichts Avatar von phpBuddy
    Registriert seit
    Aug 2004
    Ort
    Kaiserslautern
    Beiträge
    4.678

    Fortsetzung Login Systeme

    Variante Profi

    Die Variante Profi legt Wert auf einen möglichst hohen Sicherheitgrad. Manche der hier gezeigten Dinge sind auf Mietwebspace meistens, falls überhaupt, nur bei sehr guten Providern möglich, da sie ein Anpassen der php.ini erfordern. Schauen wir uns zunächst einmal das Listing der Datei login_profi.php an. Danach erläutere ich die einzelnen Schritte und binde eigene Funktionen mit in die Erklärung ein. Sämtliche Funktionen wurden in die Datei funktionen.inc.php ausgelagert. Das komplette Script gibt es am Ende des Tutorials noch einmal als Download zum selber testen. Inhalt der Datei login_profi.php:

    PHP-Code:
    <?php

    // Fehlermeldungen unterdrücken
    error_reporting);

    // Erzwingen das Session-Cookies benutzt werden und die SID nicht per URL transportiert wird
    ini_set'session.use_only_cookies''1' );
    ini_set'session.use_trans_sid''0' );

    // Session starten
    session_start();

    // Sicherstellen das die SID durch den Server vergeben wurde
    // um einen möglichen Session Fixation Angriff unwirksam zu machen
    if (!isset( $_SESSION['server_SID'] ))
    {
        
    // Möglichen Session Inhalt löschen
        
    session_unset();
        
    // Ganz sicher gehen das alle Inhalte der Session gelöscht sind
        
    $_SESSION = array();
        
    // Session zerstören
        
    session_destroy();
        
    // Session neu starten
        
    session_start();
        
    // Neue Server-generierte Session ID vergeben
        
    session_regenerate_id();
        
    // Status festhalten
        
    $_SESSION['server_SID'] = true;
    }

    // Funktionen einbinden
    include( 'funktionen.inc.php' );

    // Variablen deklarieren
    $_SESSION['angemeldet'] = false;
    $conid                  '';
    $eingabe                = array();
    $anmeldung              false;
    $update                 false;
    $fehlermeldung          '';

    // Datenbankverbindung öffnen
    $conid db_connect();

    // Wenn das Formular abgeschickt wurde
    if (isset( $_POST['login'] ))
    {
        
    // Benutzereingabe bereinigen
        
    $eingabe cleanInput();
        
    // Benutzer anmelden
        
    $anmeldung loginUser$eingabe['benutzername'], $eingabe['passwort'], $conid );
        
    // Anmeldung war korrekt
        
    if ($anmeldung)
        {
            
    // Benutzer Identifikationsmerkmale in DB speichern
            
    $update updateUser$eingabe['benutzername'], $conid );
            
    // Bei erfolgreicher Speicherung
            
    if ($update)
            {
                
    // Auf geheime Seite weiterleiten
                
    header'location: geheim_profi.php' );
                exit;
            }
            else
            {
                
    $fehlermeldung '<h3>Bei der Anmeldung ist ein Problem aufgetreten!</h3>';
            }
        }
        else
        {
            
    $fehlermeldung '<h3>Die Anmeldung war fehlerhaft!</h3>';
        }
    }

    ?>
    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="de" lang="de">
    <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <title>phpBuddy.eu - Login Script</title>
    </head>

    <body>

    <?php
    // Falls die Fehlermeldung gesetzt ist
    if ($fehlermeldung) echo $fehlermeldung;
    ?>

    <!-- Hier steht die Login-Form -->

    </body>
    </html>
    Als erstes setzen wir das Error Reporting auf 0, damit sämtliche Fehlermeldungen unterdrückt werden. So gut Fehlermeldungen während der Entwicklung sind, so schädlich können sie im live Einsatz sein. Fehlermeldungen verraten einem Angreifer sehr viel über die Struktur einer Datei, Datenbank, Funktionsweise des Scripts und erleichtern somit seine Arbeit.
    Die nächsten beiden Zeilen betreffen die php.ini. Damit wird erzwungen, dass die Session Kennung via Session Cookie transportiert wird. Auf diese Weise kann man keine Referrer mit sichtbarer Kennung in fremden Logfiles hinterlassen, was zu gestohlenen Sessions (Session Hijacking) führen kann. Ob und in welchem Umfang man Zugriff auf die php.ini hat, ist von Provider zu Provider unterschiedlich. Manche erlauben ein verändern der Werte via ini_set() oder via .htaccess, während wieder andere Provider eigene php.ini Dateien direkt im Verzeichnis erlauben oder man kann über die Accountverwaltung Werte anpassen. Die Frage ob und wie das bei Euch möglich ist kann nur euer Provider beantworten!
    Dann folgt das obligatorische starten der Session. Da ein Benutzer mit einer bereits aktiven Session auf unsere Seite kommen kann und unser session_start() in diesem Fall lediglich die Session fortführen würde, müssen wir sicherstellen, dass die Session auch tatsächlich zuvor von unserem Server initiiert wurde und nicht aus fremder Quelle übergeben wurde. Dieses "aus fremder Quelle" übergeben einer Session Kennung an ein potentielles Opfer nennt sich Session Fixation. Ein Angreifer kann eine Session nur dann übernehmen/stehlen, wenn die Session Kennung bekannt ist. Da der Angreifer diese Kennung selbst erzeugt haben kann und an sein Opfer weitergegeben hat, kennt er die Kennung natürlich! Um das zu verhindern stellen wir sicher, dass nur eine von uns selbst vergebene Kennung Gültigkeit hat.
    Dazu prüfen wir, ob in der Session die Variable $_SESSION['server_SID'] den Wert true hat. Trifft das nicht zu, resetten wir die Session, wandeln die Session in ein leeres Array um, zerstören die Session, starten die Session komplett neu, generieren eine neue Kennung (dem session_regenerate_id() fällt eine besonders wichtige Rolle zu, auf die ich weiter unten noch detaillierter eingehen werde) und setzen letztendlich den Wert der Variable $_SESSION['server_SID'] auf true.
    Durch das starten, vollständige zerstören, neu starten der Session stellen wir sicher, dass unser Script gegen Session Fixation abgesichert ist!
    Jetzt binden wir die benötigten Funktionen ein, bereiten einige Variablen vor und öffnen eine Datenbankverbindung, deren Handle wir in $conid ablegen.

    Wir prüfen ob das Forumlar abgeschickt wurde und falls ja, bereinigen wir erst einmal die Benutzereingabe.
    PHP-Code:
    $eingabe cleanInput(); 
    Die dazugehörige Funktion sieht so aus:

    PHP-Code:
    function cleanInput()
    {
        
    // Maskierende Slashes aus POST Array entfernen
        
    if (get_magic_quotes_gpc())
        {
            
    $eingabe['benutzername'] = stripslashes$_POST['benutzer'] );
            
    $eingabe['passwort']     = stripslashes$_POST['passwort'] );
        }
        else
        {
            
    $eingabe['benutzername'] = $_POST['benutzer'];
            
    $eingabe['passwort']     = $_POST['passwort'];
        }
        
    // Trimmen
        
    $eingabe['benutzername'] = trim$eingabe['benutzername'] );
        
    $eingabe['passwort']     = trim$eingabe['passwort'] );
        
    // In Kleinschrift umwandeln
        
    $eingabe['benutzername'] = strtolower$eingabe['benutzername'] );
        
    // Eingabe zurückgeben
        
    return $eingabe;

    Große Erklärungen sollten nicht nötig sein. Die Eingabe wird aufbereitet und ein Array mit dem Benutzername und Passwort zurückgeliefert, wonach im öffentlichen Bereich des Scripts fortan das Array $eingabe zur Verfügung steht.

    Nun wird geprüft, ob ein entsprechender Benutzer in unserer Datenbank existiert.
    PHP-Code:
    $anmeldung loginUser$eingabe['benutzername'], $eingabe['passwort'], $conid ); 
    Die Funktion loginUser() erwartet 3 Parameter: 1) Benutzername 2) Passwort 3) Verbindungskennung. Die Rückgabewert wird in $anmeldung abgelegt und kann entweder true (Benutzer gefunden) oder false (Login fehlerhaft) sein.
    Diese Funktion loginUser() ist der größte Brocken im ganzen Script, also werfen wir mal einen Blick auf das, was in der Funktion passiert.

    PHP-Code:
    function loginUser$benutzer$passwort$conid )
    {
        
    // Anweisung zusammenstellen
        
    $sql "SELECT
                    `passwort_zusatz`
                FROM
                    `login_profi`
                WHERE
                    LOWER(`benutzername`) = '" 
    .mysql_real_escape_string$benutzer ). "' AND
                    `aktiviert` = 1"
    ;
        
    // Anweisung an DB schicken
        
    $ergebnis mysql_query$sql$conid );
        
    // Wurde ein Datensatz gefunden, existiert dieser Benutzername, also
        // prüfen wir ob die Anmeldedaten korrekt ist
        
    if (mysql_num_rows($ergebnis) == 1)
        {
            
    $datensatz mysql_fetch_array$ergebnis );
            
    // Resourcen freigeben
            
    mysql_free_result$ergebnis );
            
    // Anmeldepasswort vorbereiten
            
    $zusatz    $datensatz['passwort_zusatz'];
            
    $anmeldepw md5$passwort.$zusatz );
            
    // Anweisung zusammenstellen
            
    $sql "SELECT
                        `id`, `fehlversuche`
                    FROM
                        `login_profi`
                    WHERE
                        LOWER(`benutzername`) = '" 
    .mysql_real_escape_string$benutzer ). "' AND
                        `passwort` = '" 
    .mysql_real_escape_string$anmeldepw ). "' AND
                        `aktiviert` = 1"
    ;
            
    // Anweisung an DB schicken
            
    $ergebnis mysql_query$sql$conid );
            
    // Prüfen ob ein Datensatz gefunden wurde. In dem Fall stimmen die Anmeldedaten
            
    if (mysql_num_rows$ergebnis ) == 1)
            {
                
    // Counter für Fehlversuche resetten
                
    $angriff mysql_fetch_array$ergebnis );
                if (
    $angriff['fehlversuche'] != 0)
                {
                    
    $sql "UPDATE
                                `login_profi`
                            SET
                                `fehlversuche` = 0
                            WHERE
                                LOWER(`benutzername`) = '" 
    .mysql_real_escape_string$benutzer ). "'
                            LIMIT
                                1"
    ;
                    
    mysql_query$sql$conid );
                }
                
    // Resourcen freigeben
                
    mysql_free_result$ergebnis );
                
    // Korrekte Anmeldung zurückgeben
                
    return true;
            }
            else
            {
                
    // Das angegebene Passwort war nicht korrekt, also gehen wir von einem Angriffsversuch aus
                // und erhöhen den Counter der fehlerhaften Anmeldeversuche
                
    $sql "UPDATE
                            `login_profi`
                        SET
                            `fehlversuche` = `fehlversuche` + 1
                        WHERE
                            LOWER(`benutzername`) = '" 
    .mysql_real_escape_string$benutzer ). "'
                        LIMIT
                            1"
    ;
                
    mysql_query$sql$conid );
                
    // Abfragen ob das Limit von 10 Fehlversuche erreicht wurde und in diesem Fall ...
                
    $sql "SELECT
                            `fehlversuche`
                        FROM
                            `login_profi`
                        WHERE
                            LOWER(`benutzername`) = '" 
    .mysql_real_escape_string$benutzer ). "'";
                
    $ergebnis mysql_query$sql$conid );
                
    $anzahl mysql_fetch_array$ergebnis );
                
    mysql_free_result$ergebnis );
                
    // ... das Konto deaktivieren
                
    if ($anzahl['fehlversuche'] > 9)
                {
                    
    $sql "UPDATE
                                `login_profi`
                            SET
                                `fehlversuche` = 0,
                                `aktiviert` = 0
                            WHERE
                                LOWER(`benutzername`) = '" 
    .mysql_real_escape_string$benutzer ). "'
                            LIMIT
                                1"
    ;
                    
    mysql_query$sql$conid );
                }
            }
        }

    Als erstes stellen wir eine Anweisung zusammen mit der wir das Feld passwort_zusatz auslesen, das zum Datensatz des Benutzername gehört und welches aktiviert=1 hat. Der Benutzername muß in der DB vom Typ Unique sein, damit es hier keine Überschneidungen gibt. Dieser Passwort-Zusatz wird auch Salt (eng. Salz) genannt und dient dazu Passwörter sicherer zu machen. Dieses Salt muß für jeden Benutzer einmalig zufällig erstellt werden (beim anlegen des Benutzer) und wird in der Datenbank abgelegt. Wurde von der DB ein Treffer zurückgeliefert wissen wir, das der Benutzername in unserem System existiert. Aus der Kombination eingegebenes Passwort + Salt aus der DB erzeugen wir einen md5-Hash, der unser tatsächliches Anmeldepasswort darstellt.

    Klären wir noch schnell die Frage "Was soll das mit dem Salt?".
    Wenn mehrere Benutzer das selbe Passwort haben, so ist der md5-Hash immer identisch. Kennt man also ein Passwort mit zugehörigem Hash und sieht irgendwo diesen Hash wieder weiß man, dass der andere Benutzer das selbe Passwort verwendet. Der Angreifer hat also keinerlei Arbeit sich Zugang zu einem Account zu verschaffen. Um es den Angreifern noch einfacher zu machen gibt es sogenannte Rainbow Tables, in denen etliche Millionen Hashes gespeichert sind, mit denen man binnen Sekunden die echte Identität eines Passwort ermitteln kann. Weil wir aber einen zufälligen Salt mit in's Passwort mischen, ist der Hash jedesmal anders, selbst wenn das reine Passwort identisch ist.
    Lange Rede kurzer Sinn: Passwörter anhand ihres Hash zu erraten oder mithilfe von Rainbow Tables zurückzuwandeln ist unmöglich, wenn man Passwörter mit einem geheimen Salt versieht und den damit erzeugten Hash in der DB ablegt. Passwörter sollten niemals im Klartext gespeichert werden!

    Zurück zum Script ...
    Wir haben jetzt also das Salt ausgelesen und unser Anmeldepasswort erzeugt. Wir schicken jetzt eine neue Anweisung an die Datenbank und lesen das Feld fehlversuche aus, das zum Datensatz gehört auf den der Benutzername + Anmeldepasswort passt. Haben wir einen Treffer, stimmen die Anmeldedaten offensichtlich überein und es handelt sich hier um unseren User. Wir resetten noch kurz den Wert von fehlversuche, falls dieser nicht 0 ist und melden mit return true; die erfolgreiche Anmeldung an's Hauptscript.
    Haben wir bei der Abfrage mit dem Benutzername + Passwort keinen Treffer erzielt wissen wir, dass zwar der Benutzername stimmt, nicht aber das Passwort! Da wir auf Sicherheit setzen, gehen wir also vom Schlimmsten aus und denken erst mal, dass es sich um einen Angriff handelt. Deswegen aktualisieren wir im else-Zweig das Feld fehlversuche und erhöhen den Counter um 1. Anschließend lesen wir das Feld fehlversuche erneut aus um zu sehen, ob das Limit Fehlversuche (in unserem Fall 10) erreicht wurde. Da sich vermutlich kein Mensch 10 mal beim eigenen Passwort verschreibt, hat hier jemand versucht mit Brute Force das Passwort zu knacken. Um dem einen Riegel vorzuschieben, deaktivieren wir das Benutzerkonto, indem wir aktiviert auf 0 setzen. Jetzt kann unser Angreifer so lange probieren wie er möchte, er wird sich nicht einloggen können, selbst wenn das Passwort korrekt wäre. Wer aufgepasst hat wird gesehen haben, dass wir bei jeder DB Abfrage immer den aktiviert Status mit einbezogen haben. Ist das Konto deaktiviert, kann man gar nichts machen, bis das Konto vom Admin wieder aktiviert wurde.


    Wir haben nun also geklärt ob die eingegebenen Benutzerdaten korrekt waren oder nicht, oder ob jemand versucht hat das Konto zu hacken. Demnach haben wir im Hauptscript in $anmeldung jetzt ein true oder false stehen. Im Falle von true geht's weiter mit
    PHP-Code:
    $update updateUser$eingabe['benutzername'], $conid ); 
    In der Funktion updateUser() findet die eigentliche Anmeldung statt. Zusätzlich speichern wir noch verschiedene Daten, anhand deren wir den Benutzer während des Aufenthalts auf unserer Seite identifizieren.

    PHP-Code:
    function updateUser$benutzer$conid )
    {
        
    // Benutzer-Datensatz aktualisieren
        
    $sql "UPDATE
                    `login_profi`
                SET
                    `ip` = '" 
    .mysql_real_escape_string$_SERVER['REMOTE_ADDR'] ). "',
                    `benutzerinfo` = '" 
    .mysql_real_escape_string$_SERVER['HTTP_USER_AGENT'] ). "',
                    `anmeldung` = '" 
    .mysql_real_escape_stringmd5$_SERVER['REQUEST_TIME'] ) ). "',
                    `zuletzt_aktiv` = NOW()
                WHERE
                    LOWER(`benutzername`) = '" 
    .mysql_real_escape_string$benutzer ). "'
                LIMIT
                    1"
    ;
        
    mysql_query$sql$conid );
        
    // Prüfen ob der datensatz aktualisiert wurde
        
    if (mysql_affected_rows$conid ) == 1)
        {
            
    // Session Variablen setzen
            
    $_SESSION['angemeldet']   = true;
            
    $_SESSION['benutzername'] = $benutzer;
            
    $_SESSION['anmeldung']    = md5$_SERVER['REQUEST_TIME'] );
            return 
    true;
        }

    Wie wir sehen aktualisieren wir den Benutzerdatensatz und speichern neben der IP Adresse auch die Identifikationsmerkmale des Browsers (Type, OS, Build Version, usw.). Da die IP Adresse generell eher ungeeignet ist als Identifikationsmerkmal, und auch die Browserkennung nicht einmalig ist, speichern wir auch den md5-Hash der Anmeldezeit. Dieser Wert dürfte, in Verbindung mit dem Benutzername, ziemlich zuverlässig sein. Wurde der Datensatz erfolgreich aktualisiert, findet erst jetzt die eigentliche Anmeldung statt!
    Nun setzen wir die Session Variablen $_SESSION['angemeldet'] auf true, sowie den Benutzername und die Anmeldezeit und liefern ein true in's Hauptscript zurück. Anschließend leiten wir den Benutzer mit einer header()-Anweisung zur geheimen Seite weiter.

    Wichtig: Wenn Fehler auftreten, sei es falsches Passwort oder der Benutzername ist unbekannt, teilen wir lediglich mit das bei der Anmeldung ein Fehler aufgetreten ist, nicht aber welcher! Würden ein Angreifer versuchen den Account mit dem Name Peterchen zu hacken und das System meldet "Benutzer unbekannt", bräuchte der Angreifer gar nicht weiter versuchen. Wieso sollte er auch einen Account hacken, der gar nicht existiert?! Versucht er nun sein Glück mit dem Account Fritzchen und auf einmal taucht die Meldung "Passwort ist falsch" auf, wo worher immer "Benutzer unbekannt" stand, weiß der Angreifer, dass dieses Konto tatsächlich existiert und kann sich an's knacken des Passworts machen.


    Okay, der Benutzer ist nun auf der geheimen Seite geheim_profi.php:

    PHP-Code:
    <?php

    ini_set
    'session.use_only_cookies''1' );
    ini_set'session.use_trans_sid''0' );

    // Session starten
    session_start();

    // Funktionen einbinden
    include( 'funktionen.inc.php' );

    // Datenbankverbindung öffnen
    $conid db_connect();

    // Benutzer prüfen
    if (!checkUser$conid ))
    {
        
    resetUser();
    }

    // Benutzer abmelden
    if ($_GET['benutzer'] == 'abmelden')
    {
        
    resetUser();
    }

    ?>
    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="de" lang="de">
    <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <title>phpBuddy.eu - Geheime Seite</title>
    </head>

    <body>

    <h3>Willkommen im geschützten Bereich! ;-)</h3>
    <p><a href="<?php echo $_SERVER['PHP_SELF']. "?benutzer=abmelden"?>">Benutzer abmelden</a></p>

    </body>
    </html>
    Der obere PHP Teil, der so auf jeder (!) geschützten Seite stehen muß, ist bereits vertraut und bedarf (hoffentlich) keiner Worte!
    Hier kontrollieren wir mithilfe von
    PHP-Code:
    if (!checkUser$conid )) 
    ob der Benutzer sich auf dieser Seite aufhalten darf. Die Funktion checkUser() hat folgenden Inhalt:

    PHP-Code:
    function checkUser$conid )
    {
        
    // Alte Session löschen und Sessiondaten in neue Session transferieren
        
    session_regenerate_idtrue );
        if (
    $_SESSION['angemeldet'] !== true) return false;
        
    // Benutzerdaten aus DB laden
        
    $sql "SELECT
                    `ip`, `benutzerinfo`, `anmeldung`, UNIX_TIMESTAMP(`zuletzt_aktiv`) as zuletzt_aktiv
                FROM
                    `login_profi`
                WHERE
                    `benutzername` = '" 
    .mysql_real_escape_string$_SESSION['benutzername'] ). "' AND
                    `aktiviert` = 1"
    ;
        
    $ergebnis mysql_query$sql$conid );
        if (
    mysql_num_rows$ergebnis ) == 1)
        {
            
    $benutzerdaten mysql_fetch_array$ergebnis );
            
    // Resourcen freigeben
            
    mysql_free_result$ergebnis );
            
    // Daten aus der DB mit den Benutzerdaten vergleichen
            
    if ($benutzerdaten['ip'] != $_SERVER['REMOTE_ADDR']) return false;
            if (
    $benutzerdaten['benutzerinfo'] != $_SERVER['HTTP_USER_AGENT']) return false;
            if (
    $benutzerdaten['anmeldung'] != $_SESSION['anmeldung']) return false;
            if ((
    $benutzerdaten['zuletzt_aktiv'] + 600) <= $_SERVER['REQUEST_TIME']) return false;
        }
        else
        {
            return 
    false;
        }
        
    // Letzte Aktivität aktualisieren
        
    $sql "UPDATE
                    `login_profi`
                SET
                    `zuletzt_aktiv` = NOW()
                WHERE
                    LOWER(`benutzername`) = '" 
    .mysql_real_escape_string$_SESSION['benutzername'] ). "'
                LIMIT
                    1"
    ;
        
    mysql_query$sql$conid );
        
    // Status zurückgeben
        
    return true;

    Das erste das in der Funktion geschieht ist
    PHP-Code:
    session_regenerate_idtrue ); 
    Diese Zeile ist ungemein wichtig! Diese PHP Funktion sorgt dafür, dass eine neue Session Kennung initiiert wird und der Inhalt der alten Session in die neue übernommen wird. Der Parameter true existiert erst seit PHP 5.2.x und sorgt dafür, dass nicht nur eine neue Kennung generiert wird, sondern es werden auch sämtliche alten Reste der vorherigen Session gelöscht. Das erhöht die Sicherheit von Sessions deutlich, da es nicht mehr möglich ist, dass sich Unbefugte über einen Shell-Zugriff die alte Session Datei auf dem Server aneignen und auslesen und so u.U. an geheime Informationen gelangen.

    Als nächstes wird geprüft, ob der Benutzr mit $_SESSION['angemeldet'] angemeldet ist. Nun lesen wir die gespeicherten Vergleichsmerkmale aus der DB aus und vergleichen jeden dieser Punkte mit dem aktuell angemeldeten Benutzer. Stimmt ein Wert nicht überein, liefern wir false zurück. In der Zeile
    PHP-Code:
    if (($benutzerdaten['zuletzt_aktiv'] + 600) <= $_SERVER['REQUEST_TIME']) return false
    prüfen wir zusätzlich, wie lange die letzte Aktivität auf der Seite zurückliegt. Wurde für 10 Minuten keine Aktivität registriert, wird der Benutzer zwangsabgemeldet. Das macht man deswegen, weil es Benutzer gibt die sich anmelden und dann auf anderen Seiten surfen oder den Rechner verlassen, während der Browser geöffnet bleibt. Das Problem dabei ist, dass die Session erst erlischt, wenn der Browser geschlossen wird. Mit anderen Worten: der Benutzer bleibt so lange angemeldet, bis er den Browser schließt, selbst wenn er schon lange nicht mehr auf der Seite war. Es könnte also jemand die Session übernehmen, während die Session aktiv ist. Durch das 10 Minuten Limit begrenzen wir das Zeitfenster in dem Schaden entstehen könnte.

    Wurde die Funktion bisher nicht durch ein return false; verlassen, handelt es sich um den echten Benutzer und wir aktualisieren den Datensatz dahingehend, dass wir die Zeit der letzten Aktivität festhalten.

    Kommen wir zur letzten Funktion. Wurde ein false zurückgeliefert oder der Benutzer klickt den "Abmelden"-Link, wird die Funktion resetUser() aufgerufen:

    PHP-Code:
    function resetUser()
    {
        
    session_destroy();
        
    header'location: login_profi.php' );
        exit;

    Hier geschieht nichts spektakuläres. Es wird lediglich der Inhalt der Session gelöscht und auf die Login Seite umgeleitet. Auf der Login Seite angekommen treten wieder unsere Anmelde- und Schutzmechanismen in Aktion.

    Hinweis:
    Noch ein Wort zu den Erkennungsmerkmalen eines Benutzers. Während die Browserkennung als zusätzliches Merkmal durchaus brauchbar ist, auch wenn viele den selben Browser und OS verwenden, gibt es doch erstaunlich häufig Abweichungen in SP Nummern, Build Versionen, etc., so ist die IP in den meisten Fällen ungeeignet!
    Zum einen kann es sein das Benutzer durch Proxies surfen und sich somit ständig die IP ändert (z.B. AOL User benutzen zwangsweise Proxies, weil AOL es seinen Benutzern diktiert!), zum anderen sind speziell in Firmen meistens NAT Netzwerke installiert, wodurch sich eine handvoll bis mehrere Hundert oder Tausend Benutzer eine identische IP teilen. Man sollte sich also überlegen, ob man die IP zur Identifikation benutzen möchte!
    Auf jeden Fall sollte aber noch etwas einmaliges zur Identifizierung genommen werden. In unserem Beispiel ist das der Benutzername in Kombination mit dem Hash der Anmeldezeit.



    Fazit

    Damit kommen wir zum Ende dieses Tutorials. Es wurde mal wieder länger als geplant, aber beim Thema Sicherheit sollte man das verzeihen können.
    Bleibt zu hoffen, dass der ein oder andere etwas damit anfangen kann und die eigene Seite damit etwas sicherer wird. Um Missverständnisse auszuschließen hier noch der Hinweis:
    Dieses Tutorial soll als Grundlage für eigene Login System dienen und ist nicht dazu gedacht, dass es genau so 1:1 übernommen wird! Ebenso geschieht die Benutzung auf eigene Gefahr und weder das Traum-Projekt noch der Autor dieses Tutorials können für Schäden, die durch das Script entstanden sein könnten, haftbar gemacht werden!

    Zum Abschluß noch einige Links zu den verwendeten Funktionen und weiterführende Informationen zum Thema Sessions und Sicherheit.


    Linkübersicht

    Sessions und Sicherheit

    PHP Funktionen
    Angehängte Dateien
    Geändert von phpBuddy (02.08.2008 um 14:23 Uhr)

  3. #3
    TP-Moderator StephanF ist ein richtiges Arbeitstier - DANKE StephanF ist ein richtiges Arbeitstier - DANKE StephanF ist ein richtiges Arbeitstier - DANKE StephanF ist ein richtiges Arbeitstier - DANKE Avatar von StephanF
    Registriert seit
    Feb 2003
    Ort
    Erfurt
    Beiträge
    1.217

    Thumbs up

    Respekt, da hat sich der "Buddy" mal wieder was richtig brauchbares einfallen lassen. Und wie immer auch gut ge- und beschrieben. Danke Dir Rizzo hierfür
    Schöne Grüße aus Thüringen
    Stephan Page

    Stell Dir vor, hier steht was und keiner liest es!! schon entdeckt?? F1 ist ne geile Taste
    Ich beantworte keine E-Mails. Bitte alle Fragen ins Forum

    schon gehört??? Das Internet ist voll, die lassen keinen mehr rein!!

  4. #4
    TP-Insider TP-Sponsor zulujaner ist ein richtiges Arbeitstier - DANKE zulujaner ist ein richtiges Arbeitstier - DANKE zulujaner ist ein richtiges Arbeitstier - DANKE zulujaner ist ein richtiges Arbeitstier - DANKE Avatar von zulujaner
    Registriert seit
    Apr 2008
    Ort
    Naumburg
    Beiträge
    879

    Lightbulb

    Moin,

    Login System - Prima Sache. Kann man immer gebrauchen.

    Kann mich nur anschliessen, super beschrieben, alles dabei.
    Auch an die faulen Tipper gedacht

    Danke für Ergebnis und die Mühen, werds mal probieren...

  5. #5
    TP-Specialist phpBuddy ist einer der Eckpfeiler des TP - ohne ihn geht nichts phpBuddy ist einer der Eckpfeiler des TP - ohne ihn geht nichts phpBuddy ist einer der Eckpfeiler des TP - ohne ihn geht nichts phpBuddy ist einer der Eckpfeiler des TP - ohne ihn geht nichts phpBuddy ist einer der Eckpfeiler des TP - ohne ihn geht nichts phpBuddy ist einer der Eckpfeiler des TP - ohne ihn geht nichts phpBuddy ist einer der Eckpfeiler des TP - ohne ihn geht nichts phpBuddy ist einer der Eckpfeiler des TP - ohne ihn geht nichts phpBuddy ist einer der Eckpfeiler des TP - ohne ihn geht nichts phpBuddy ist einer der Eckpfeiler des TP - ohne ihn geht nichts phpBuddy ist einer der Eckpfeiler des TP - ohne ihn geht nichts Avatar von phpBuddy
    Registriert seit
    Aug 2004
    Ort
    Kaiserslautern
    Beiträge
    4.678
    Danke für die Blumen. Aber gibt nichts zu danken, weil das ja jetzt nichts sooo großes ist. Der dickste Brocken liegt mit Teil 2 aber noch vor mir, weil das Listing der funktionen.inc.php schon fast länger ist als der gesamte 1. Teil dieses Tutorials.


    p.s.: Jaaa ich weiß Mark, ich hab's ja versucht kurz zu halten, aber ich kann's eben nicht besser.

  6. #6
    TP-Specialist phpBuddy ist einer der Eckpfeiler des TP - ohne ihn geht nichts phpBuddy ist einer der Eckpfeiler des TP - ohne ihn geht nichts phpBuddy ist einer der Eckpfeiler des TP - ohne ihn geht nichts phpBuddy ist einer der Eckpfeiler des TP - ohne ihn geht nichts phpBuddy ist einer der Eckpfeiler des TP - ohne ihn geht nichts phpBuddy ist einer der Eckpfeiler des TP - ohne ihn geht nichts phpBuddy ist einer der Eckpfeiler des TP - ohne ihn geht nichts phpBuddy ist einer der Eckpfeiler des TP - ohne ihn geht nichts phpBuddy ist einer der Eckpfeiler des TP - ohne ihn geht nichts phpBuddy ist einer der Eckpfeiler des TP - ohne ihn geht nichts phpBuddy ist einer der Eckpfeiler des TP - ohne ihn geht nichts Avatar von phpBuddy
    Registriert seit
    Aug 2004
    Ort
    Kaiserslautern
    Beiträge
    4.678
    Teil 2 (Variante Profi) hinzugefügt.

    Viel Spaß mit dem Tutorial.

  7. #7
    TP-Insider Sand*mann hilft, wo's geht Sand*mann hilft, wo's geht Avatar von Sand*mann
    Registriert seit
    Nov 2005
    Ort
    Berlin
    Beiträge
    585
    Da fehlen mir die Worte
    Außer ein riiieeeeeeeesen DANKE SCHÖN
    Geändert von Sand*mann (29.07.2008 um 03:41 Uhr)

  8. #8
    TP-Specialist phpBuddy ist einer der Eckpfeiler des TP - ohne ihn geht nichts phpBuddy ist einer der Eckpfeiler des TP - ohne ihn geht nichts phpBuddy ist einer der Eckpfeiler des TP - ohne ihn geht nichts phpBuddy ist einer der Eckpfeiler des TP - ohne ihn geht nichts phpBuddy ist einer der Eckpfeiler des TP - ohne ihn geht nichts phpBuddy ist einer der Eckpfeiler des TP - ohne ihn geht nichts phpBuddy ist einer der Eckpfeiler des TP - ohne ihn geht nichts phpBuddy ist einer der Eckpfeiler des TP - ohne ihn geht nichts phpBuddy ist einer der Eckpfeiler des TP - ohne ihn geht nichts phpBuddy ist einer der Eckpfeiler des TP - ohne ihn geht nichts phpBuddy ist einer der Eckpfeiler des TP - ohne ihn geht nichts Avatar von phpBuddy
    Registriert seit
    Aug 2004
    Ort
    Kaiserslautern
    Beiträge
    4.678





    EDIT
    Passend zum 3. Beispiel ganz aktuell die Meldung auf heise Security: Apples Webbrowser Safari anfällig für Session-Fixation-Angriffe
    Im Artikel heißt es unter anderem:
    Allerdings hängt der Erfolg dieser auch Session Fixation genannten Attacke von der konkrekten Implementierung der Web-Anwendung ab, beispielsweise ob noch die IP-Adresse oder andere Informationen in die Sitzungsdaten eingehen.
    Geändert von phpBuddy (29.07.2008 um 05:11 Uhr)

  9. #9
    TP-Newbie M.Bü macht alles soweit korrekt Avatar von M.Bü
    Registriert seit
    Sep 2008
    Ort
    Dessau-Roßlau
    Beiträge
    3
    Echt klasse Tutorial! (Wie die anderen von dir übrigens auch, hab hier die letzten Tage mal ein bisschen rumgestöbert)

    Eine Frage hätte ich zur Profivariante...

    $_SESSION['server_SID']

    Ich versteh nicht inwiefern das die Session Fixation unterbindet... der Angreifer kann doch in seiner eigenen Session diesen Wert auch speichern... er muss das Script dazu natürlich kennen... aber mit ein paar Tricks sollte es einem guten Angreifer doch möglich sein mal zu gucken, was bei einem legal angemeldeten User in der Session drin steht (wenn er z.B. selbst Mitglied ist), oder?

    Hoffe meine Frage ist verständlich

    mfg
    Martin

  10. #10
    TP-Specialist phpBuddy ist einer der Eckpfeiler des TP - ohne ihn geht nichts phpBuddy ist einer der Eckpfeiler des TP - ohne ihn geht nichts phpBuddy ist einer der Eckpfeiler des TP - ohne ihn geht nichts phpBuddy ist einer der Eckpfeiler des TP - ohne ihn geht nichts phpBuddy ist einer der Eckpfeiler des TP - ohne ihn geht nichts phpBuddy ist einer der Eckpfeiler des TP - ohne ihn geht nichts phpBuddy ist einer der Eckpfeiler des TP - ohne ihn geht nichts phpBuddy ist einer der Eckpfeiler des TP - ohne ihn geht nichts phpBuddy ist einer der Eckpfeiler des TP - ohne ihn geht nichts phpBuddy ist einer der Eckpfeiler des TP - ohne ihn geht nichts phpBuddy ist einer der Eckpfeiler des TP - ohne ihn geht nichts Avatar von phpBuddy
    Registriert seit
    Aug 2004
    Ort
    Kaiserslautern
    Beiträge
    4.678
    Hallo Martin,

    Wenn man nur schaut ob die Session existiert, ist noch lange nicht gewährleistet das sie auch vom Server initialisiert wurde. Wenn Du eine Seite z.B. mit index.php?PHPSESSIONID=Hamster aufrufst, legt der Server u.U. eine Session mit dem Name Hamster an. Schickt man einem Opfer einen Link mit diesem Anhang, übernimmt der die von Dir gestartete Session.
    Dadurch das wir die Session zuerst killen und anschließend eine neue vom Server erzeugen, verhindert man, dass ein User bereits mit einer aktiven Session auf die Seite kommt. Schau doch mal im Anhang in die Links, dort wird das ganz gut erklärt.
    So ohne weitere kann ein Angreifer auch nicht mal eben in die Session schauen, weil diese Informationen auf dem Webserver abgelegt sind und nicht beim Benutzer auf dem Rechner. Auch das klauen der Session ID ist so nicht möglich, da bei jedem Aufruf eine neue generiert wird, die zu diesem Zeitpunkt ebenfalls nur dem Server bekannt ist. Selbst wenn jemand die ID über die Adresse auslesen könnte, was wir aber auch unterbunden haben, würde er immer nur die vorherige ID bekommen, die zu diesem Zeitpunkt schon nicht mehr aktuell ist.

  11. #11
    TP-Newbie M.Bü macht alles soweit korrekt Avatar von M.Bü
    Registriert seit
    Sep 2008
    Ort
    Dessau-Roßlau
    Beiträge
    3
    Hm... ok... ich glaube die Probleme, die ich damals mit meinem Script hatte, werden bei dir an anderer Stelle behoben...

    ich hatte folgendes Problem... ich habe zur Erkennung nur überprüft, ob eine User-ID in der Session liegt (die wurde da halt reingesetzt, wenn man sich einloggt)... da ich dieses Login-Script aber auf zwei Seiten verwendet hatte, konnte man sich auf der einen einloggen und dann auf die andere gehen... schwups war man dort bei dem User eingeloggt, der dieselbe ID hatte, wie man selbst auf ersterer Seite...

    Und da hab ich mir halt nur grad überlegt, dass es das auch nicht sicherer gemacht hätte, wenn man zusätzlich noch auf "server_SID" prüft, da das andere Script diesen Wert ja auch setzen würde... darauf wollte ich eigtl. hinaus.

    Doch bei deinem Script wird es wohl eher schwierig über so einen Trick in einen Account zu kommen, da z.B. der Hashwert der Anmeldezeit kaum übereinstimmen wird.

    Hoffe, ich hab jetzt nicht noch mehr durcheinandergewürfelt. Ist schon arg spät ^^.

    mfg
    Martin

  12. #12
    TP-Specialist phpBuddy ist einer der Eckpfeiler des TP - ohne ihn geht nichts phpBuddy ist einer der Eckpfeiler des TP - ohne ihn geht nichts phpBuddy ist einer der Eckpfeiler des TP - ohne ihn geht nichts phpBuddy ist einer der Eckpfeiler des TP - ohne ihn geht nichts phpBuddy ist einer der Eckpfeiler des TP - ohne ihn geht nichts phpBuddy ist einer der Eckpfeiler des TP - ohne ihn geht nichts phpBuddy ist einer der Eckpfeiler des TP - ohne ihn geht nichts phpBuddy ist einer der Eckpfeiler des TP - ohne ihn geht nichts phpBuddy ist einer der Eckpfeiler des TP - ohne ihn geht nichts phpBuddy ist einer der Eckpfeiler des TP - ohne ihn geht nichts phpBuddy ist einer der Eckpfeiler des TP - ohne ihn geht nichts Avatar von phpBuddy
    Registriert seit
    Aug 2004
    Ort
    Kaiserslautern
    Beiträge
    4.678
    Hallo Martin,

    genau dieses Szenario wird damit ja unterbunden. Da wir nur Session Kennungen zulassen die vom Server vergeben wurden, wird ausgeschlossen, dass ein anderer User die Session übernehmen kann. Dieses übernehmen der Session ist das oben erwähnte Session Hijacking.
    Die Session Kennung vom Server wird zufällig generiert und die Wahrscheinlichkeit das dieser Zufallswert bei 2 Usern gleich sein kann ist 0%. Eine Session ist eine Datei die auf dem Server abgelegt wird. Es können keine 2 Dateien mit dem gleichen Name, aber unterschiedlichem Inhalt existieren.

  13. #13
    TP-Newbie M.Bü macht alles soweit korrekt Avatar von M.Bü
    Registriert seit
    Sep 2008
    Ort
    Dessau-Roßlau
    Beiträge
    3
    Ok, ich glaub ich habs verstanden... war ein technisches Problem. Ich hatte immer so die Vorstellung, dass die Session-Daten auch in einem Cookie hängen und nicht aufm Server. Dann ist auch klar, warum die Überprüfung eines Booleans ausreicht. Vielen Dank.

    eins noch: wenn ich dieses Log-In Script in zwei User-Bereichen auf demselben Server verwenden würde, wäre es nicht mehr ausreichend oder? Dann müsste man noch überprüfen ob die Session im Rahmen des entsprechenden Bereiches vergeben wurde, oder?

    mfg
    Martin

  14. #14
    TP-Junior Iron Man macht alles soweit korrekt
    Registriert seit
    Oct 2008
    Beiträge
    21
    Hallo Rizzo!

    Was für ein Tutorial. Genau danach habe ich gesucht. Du weisst wie der Hase läuft. Habe aber noch eine Frage: Du hast meine Interesse mit dem Passwort-Zusatz (Salt) geweckt. Aber du hast angesprochen das dies schon bei der Anmeldung generiert werden muss. Aber wie?

  15. #15
    TP-Specialist phpBuddy ist einer der Eckpfeiler des TP - ohne ihn geht nichts phpBuddy ist einer der Eckpfeiler des TP - ohne ihn geht nichts phpBuddy ist einer der Eckpfeiler des TP - ohne ihn geht nichts phpBuddy ist einer der Eckpfeiler des TP - ohne ihn geht nichts phpBuddy ist einer der Eckpfeiler des TP - ohne ihn geht nichts phpBuddy ist einer der Eckpfeiler des TP - ohne ihn geht nichts phpBuddy ist einer der Eckpfeiler des TP - ohne ihn geht nichts phpBuddy ist einer der Eckpfeiler des TP - ohne ihn geht nichts phpBuddy ist einer der Eckpfeiler des TP - ohne ihn geht nichts phpBuddy ist einer der Eckpfeiler des TP - ohne ihn geht nichts phpBuddy ist einer der Eckpfeiler des TP - ohne ihn geht nichts Avatar von phpBuddy
    Registriert seit
    Aug 2004
    Ort
    Kaiserslautern
    Beiträge
    4.678
    Hallo Iron Man,

    was Du als Salt benutzt spielt keine Rolle. Das kann ein Fragment von einem MD5 Hash sein, den Du aus einem Zeitstempel erzeugst, oder eine willkürliche Zahlen-/Buchstabenfolge. Wichtig ist nur, dass es einmalig in der DB vorkommt und nicht einfach so zu erraten ist.

+ Antworten
Seite 1 von 7 1 2 3 4 ... LetzteLetzte

Ähnliche Themen

  1. CRM-Systeme
    Von overflood im Forum Business allgemein
    Antworten: 3
    Letzter Beitrag: 09.11.2007, 17:16
  2. Bildergalerie Systeme
    Von Robert im Forum Webdesign allgemein
    Antworten: 3
    Letzter Beitrag: 28.06.2007, 16:24
  3. GDS-Systeme - jemand Erfahrung?
    Von Kafkaesk im Forum Business allgemein
    Antworten: 3
    Letzter Beitrag: 30.10.2005, 01:53
  4. Shop-Systeme
    Von aferber im Forum Content Management Systeme & Shop Systeme
    Antworten: 0
    Letzter Beitrag: 16.02.2005, 11:32
  5. Template Systeme
    Von Kawa im Forum Traum-Dynamik
    Antworten: 2
    Letzter Beitrag: 23.01.2004, 16:24

Stichworte


Aktive Benutzer

Aktive Benutzer

Aktive Benutzer in diesem Thema: 1 (Registrierte Benutzer: 0, Gäste: 1)

     

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51