• Jetzt anmelden. Es dauert nur 2 Minuten und ist kostenlos!

Login-Skript mit PHP und MySQL

Bourbon

Mitglied
Ich wünsche einen wunderschönen guten Morgen!

Inzwischen sitze ich daran, ein Login zu basteln bzw. mir eins aus Tutorials zurecht zu ziehen.
Ein, auf den ersten Blick gut wirkendes, Tutorial fand ich hier:
http://de.wikihow.com/Ein-sicheres-Login-Skript-mit-PHP-und-MySQL-erstellen

Leider stellt sich dabei raus, dass der Code von einigen Fehlern heimgesucht wird, bspw. Verwendung eines falschen Passwortes etc.
Habe alles 1zu1 so wie im Tutorial beschrieben gemacht und dennoch spukt es mir ohne Ende Fehler aus.

Warning: include_once(includes/register.inc.php): failed to open stream: No such file or directory in C:\xampp\htdocs\php-beispiele\MySQL-Test\root\includes\register.php on line 2

Warning: include_once(): Failed opening 'includes/register.inc.php' for inclusion (include_path='.;C:\xampp\php\PEAR') in C:\xampp\htdocs\php-beispiele\MySQL-Test\root\includes\register.php on line 2

Warning: include_once(includes/functions.php): failed to open stream: No such file or directory in C:\xampp\htdocs\php-beispiele\MySQL-Test\root\includes\register.php on line 3

Warning: include_once(): Failed opening 'includes/functions.php' for inclusion (include_path='.;C:\xampp\php\PEAR') in C:\xampp\htdocs\php-beispiele\MySQL-Test\root\includes\register.php on line 3

Um mal was zu zeigen.

Habt ihr vielleicht ein besseres Tutorial, in welchem aber ebenso diverse Sicherheitskomponenten wie bspw. das Hashen eines Passwortes enthalten sind?

Wie immer vielen Dank voraus für alle Antworten!
 
Werbung:
So weit ich das nach dem Überfliegen sagen kann, ist das Tutorial nicht so schlecht. Die Fehler die du gepostet hast sind doch relativ eindeutig. Schau, ob die benötigten Dateien dort liegen, wo sie sollen und ob dort die richtigen Zugriffsrechte eingestellt sind. Lies das Tutorial notfalls noch mal gründlich, dort wird beschrieben welche Codeschnipsel in welche Dateien gehören und wie die Struktur ist.
 
Ich wünsche einen wunderschönen guten Morgen!

Inzwischen sitze ich daran, ein Login zu basteln bzw. mir eins aus Tutorials zurecht zu ziehen.
Ein, auf den ersten Blick gut wirkendes, Tutorial fand ich hier:
http://de.wikihow.com/Ein-sicheres-Login-Skript-mit-PHP-und-MySQL-erstellen

Leider stellt sich dabei raus, dass der Code von einigen Fehlern heimgesucht wird, bspw. Verwendung eines falschen Passwortes etc.
Habe alles 1zu1 so wie im Tutorial beschrieben gemacht und dennoch spukt es mir ohne Ende Fehler aus.



Um mal was zu zeigen.

Habt ihr vielleicht ein besseres Tutorial, in welchem aber ebenso diverse Sicherheitskomponenten wie bspw. das Hashen eines Passwortes enthalten sind?

Wie immer vielen Dank voraus für alle Antworten!

Das Tutorial habe ich damals auch benutzt, schlecht ist es nicht aber für mich eindeutig viel zu viel Code. Wenn es dir um die Sicherheit geht, solltest du dich erstmal schlau machen, was es überhaupt für Sicherheitslücken gibt und wie man sich effektiv dagegen schützen kann. Bspw. SQL-Injection, Code-Injection, Cross-Site Scripting, CSRF und so weiter. Schau dich dazu einfach mal ein bisschen auf OWASP um. Hier nochmal was aus der PHP Manual zu Password Hashing.
 
Zuletzt bearbeitet:
Werbung:
Nein, ich wollte es auch nie so darstellen, dass das Tutorial schlecht ist, es hat nur hier und da einige Fehler.
Bei den oben aufgeführten Fehler handelt es sich ja eigentlich um ganz einfache Fehler, die ich aber irgendwie nicht lösen kann.
"Eigentlich" liegt alles da, wo es soll und dennoch kann manches nicht gefunden werden.

Ein Beispiel:
login.php - durch Drücken des Knopfes "Login" soll die Datei process_login.php im Ordner includes ausgeführt werden. Ich bekomm einen Fehler 404 obwohl die Datei auch genau da liegt und ich die Codeschnipsel auch einfach per copy&paste eingefügt habe.

Oder Teil 3 von 8 - Nummer 1
PHP:
<?php
/**
* Das sind die Login-Angaben für die Datenbank
*/ 
define("HOST", "localhost");     // Der Host mit dem du dich verbinden willst.
define("USER", "sec_user");    // Der Datenbank-Benutzername.
define("PASSWORD", "4Fa98xkHVd2XmnfK");    // Das Datenbank-Passwort.
define("DATABASE", "secure_login");    // Der Datenbankname.
define("CAN_REGISTER", "any");
define("DEFAULT_ROLE", "member");
define("SECURE", FALSE);    // NUR FÜR DIE ENTWICKLUNG!!!!

Das Password muss durch dieses hier ersetzt werden: (wem kann man das eigentlich auf der Seite mitteilen?):
Hier sind die Details der erstellten Benutzer:
  • Benutzer: "sec_user"
  • Passwort: "eKcGZr59zAa2BEWU"
 
Ein Beispiel:
login.php - durch Drücken des Knopfes "Login" soll die Datei process_login.php im Ordner includes ausgeführt werden. Ich bekomm einen Fehler 404 obwohl die Datei auch genau da liegt und ich die Codeschnipsel auch einfach per copy&paste eingefügt habe.

Dann stimmt vermutlich etwas mit den Zugriffsrechten nicht.

Das Password muss durch dieses hier ersetzt werden: (wem kann man das eigentlich auf der Seite mitteilen?):

Was meinst du? DB-Zugangsdaten? Niemandem!
 
PHP:
<?php
include_once 'includes/db_connect.php';
include_once 'includes/functions.php';
sec_session_start();
if (login_check($mysqli) == true) {
    $logged = 'in';
} else {
    $logged = 'out';
}
?>
<!DOCTYPE html>
<html>
    <head>
        <title>Secure Login: Log In</title>
        <link rel="stylesheet" href="styles/main.css" />
        <script type="text/JavaScript" src="js/sha512.js"></script>
        <script type="text/JavaScript" src="js/forms.js"></script>
    </head>
    <body>
        <?php
        if (isset($_GET['error'])) {
            echo '<p class="error">Error Logging In!</p>';
        }
        ?>
        <form action="includes/process_login.php" method="post" name="login_form">                  
            Email: <input type="text" name="email" />
            Password: <input type="password"
                             name="password"
                             id="password"/>
            <input type="button"
                   value="Login"
                   onclick="formhash(this.form, this.form.password);" />
        </form>
        <p>If you don't have a login, please <a href="includes/register.php">register</a></p>
        <p>If you are done, please <a href="includes/logout.php">log out</a>.</p>
        <p>You are currently logged <?php echo $logged ?>.</p>
    </body>
</html>

Das ist der Code von der login.php

Ich meinte ja nicht die Zugangsdaten zur eigenen Datenbank, sondern, dass im Beispiel das Passwort des Testbenutzers "sec_user" falsch ist.

//EDIT
Einen Teil der Fehler konnte ich jetzt beheben, hab die Pfade nochmals angepasst. War sicherlich ein typisches Copy&Paste-Problem.

Nur der Login-Button funktioniert überhaupt nicht.
Der macht einfach gar nichts.

Das Tutorial habe ich damals auch benutzt, schlecht ist es nicht aber für mich eindeutig viel zu viel Code.....
Hast du etwas, wo es mit weniger Code ebenfalls funktioniert?
 
Zuletzt bearbeitet:
Werbung:
Eventuell. Hab bisher noch nicht rausgefunden, woran es liegt.
Werde es aber weiter versuchen, vielleicht finde ich noch was.

Kennt ihr ausserdem vielleicht noch ein besseres oder gleichwertiges Tutorial?
 
Schau mal auf php.net, anstatt bei Tutorials Copy & Paste zu machen. Dann unterlaufen dir auch vllt keine Fehler. Das Tutorial hat damals bei mir Einwandfrei geklappt. Selbstverständlich solltest du auch wissen was du da machst, anstatt einfach alles ab zu schreiben. Soll nicht böse gemeint sein, aber so hast du definitiv einen besseren Lerneffekt.

Erstell dir doch einfach eine eigene Datenbank und bau mit mysqli_* eine Verbindung auf. Dann prüfst du mit den von PHP vorgegebenen Kontrollstrukturen deine Eingaben und validierst Sie bevor du den Query abschickst und am Ende ob deine Daten mit den Daten in der Datenbank übereinstimmen. Alles was dazu brauchst findest du in der Manual von PHP. Kein Hexenwerk. Danach hast du auch denke ich verstanden was du da machst und warum. Zum Thema Sicherheit kannst du dich danach noch schlau machen, wenn das Grundgerüst steht.
 
Werbung:
Okay, das klingt vernünftig.

Dann lass ich erstmal das fertige Konstrukt und bau mir das lieber nach und nach selbst zusammen.
Kannst du mir vielleicht ein paar wichtige Schlagworte nennen?
 
  • Html Formular
  • $_GET, $_POST, $_SESSION
  • Kontrollstrukturen
  • Datenbank
  • MySQLi
  • SQL-Query (SELECT)
Reicht dir das erstmal an Schlagworten? Wie gesagt, alles was du brauchst findest du aufjedenfall auf php.net
 
Bin auch ein Relativ unerfahrener "Webentwickler" :confused:

ich empfehle dir in deinem Browser die DevTools mit F12 zu aktivieren, das habe ich erst vor ein paar Tagen wirklich zu schätzen gelernt das Hilft mir echt gut.
 
Werbung:
ich empfehle dir in deinem Browser die DevTools mit F12 zu aktivieren, das habe ich erst vor ein paar Tagen wirklich zu schätzen gelernt das Hilft mir echt gut.
Oder man installiert sich Firebug, was ich persönlich besser finde. :p
 
Also ich sitze gerade mal wieder am Login-Skript von dieser Seite hier:
http://de.wikihow.com/Ein-sicheres-Login-Skript-mit-PHP-und-MySQL-erstellen

Inzwischen klappt es auch so, wie es laut Tutorial soll.

Aber jetzt scheitere ich bereits an dem Versuch, den Nutzername wegzulassen.
Ich verstehe aber nicht, woran das liegt, weil ich die betreffenden Stellen eigentlich entfernt haben müsste.

Hier mal die Ursprungsdateien:

register.php:
PHP:
<?php
include_once 'includes/register.inc.php';
include_once 'includes/functions.php';
?>
<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>Secure Login: Registration Form</title>
        <script type="text/JavaScript" src="js/sha512.js"></script>
        <script type="text/JavaScript" src="js/forms.js"></script>
        <link rel="stylesheet" href="styles/main.css" />
    </head>
    <body>
        <!-- Anmeldeformular für die Ausgabe, wenn die POST-Variablen nicht gesetzt sind
        oder wenn das Anmelde-Skript einen Fehler verursacht hat. -->
        <h1>Register with us</h1>
        <?php
        if (!empty($error_msg)) {
            echo $error_msg;
        }
        ?>
        <ul>
            <li>E-Mail-Adressen müssen ein gültiges Format haben.</li>
            <li>Passwörter müssen mindest sechs Zeichen lang sein.</li>
            <li>Passwörter müssen enthalten
                <ul>
                    <li>mindestens einen Großbuchstaben (A..Z)</li>
                    <li>mindestens einen Kleinbuchstabenr (a..z)</li>
                    <li>mindestens eine Ziffer (0..9)</li>
                </ul>
            </li>
            <li>Das Passwort und die Bestätigung müssen exakt übereinstimmen.</li>
        </ul>
        <form action="<?php echo esc_url($_SERVER['PHP_SELF']); ?>"
                method="post"
                name="registration_form">
            Username: <input type='text'
                name='username'
                id='username' /><br>
            Email: <input type="text" name="email" id="email" /><br>
            Password: <input type="password"
                             name="password"
                             id="password"/><br>
            Confirm password: <input type="password"
                                     name="confirmpwd"
                                     id="confirmpwd" /><br>
            <input type="button"
                   value="Register"
                   onclick="return regformhash(this.form,
                                   this.form.email,
                                   this.form.password,
                                   this.form.confirmpwd);" />
        </form>
        <p>Return to the <a href="index.php">login page</a>.</p>
    </body>
</html>

register.inc.php
PHP:
<?php
include_once 'db_connect.php';
include_once 'psl-config.php';
$error_msg = "";
if (isset($_POST['username'], $_POST['email'], $_POST['p'])) {
    // Bereinige und überprüfe die Daten
    $username = filter_input(INPUT_POST, 'username', FILTER_SANITIZE_STRING);
    $email = filter_input(INPUT_POST, 'email', FILTER_SANITIZE_EMAIL);
    $email = filter_var($email, FILTER_VALIDATE_EMAIL);
    if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
        // keine gültige E-Mail
        $error_msg .= '<p class="error">The email address you entered is not valid</p>';
    }
    $password = filter_input(INPUT_POST, 'p', FILTER_SANITIZE_STRING);
    if (strlen($password) != 128) {
        // Das gehashte Passwort sollte 128 Zeichen lang sein.
        // Wenn nicht, dann ist etwas sehr seltsames passiert
        $error_msg .= '<p class="error">Invalid password configuration.</p>';
    }
    // Benutzername und Passwort wurde auf der Benutzer-Seite schon überprüft.
    // Das sollte genügen, denn niemand hat einen Vorteil, wenn diese Regeln 
    // verletzt werden.
    //
    $prep_stmt = "SELECT id FROM members WHERE email = ? LIMIT 1";
    $stmt = $mysqli->prepare($prep_stmt);
    if ($stmt) {
        $stmt->bind_param('s', $email);
        $stmt->execute();
        $stmt->store_result();
        if ($stmt->num_rows == 1) {
            // Ein Benutzer mit dieser E-Mail-Adresse existiert schon
            $error_msg .= '<p class="error">A user with this email address already exists.</p>';
        }
    } else {
        $error_msg .= '<p class="error">Database error</p>';
    }
    // Noch zu tun:
    // Wir müssen uns noch um den Fall kümmern, wo der Benutzer keine
    // Berechtigung für die Anmeldung hat indem wir überprüfen welche Art
    // von Benutzer versucht diese Operation durchzuführen.
    if (empty($error_msg)) {
        // Erstelle ein zufälliges Salt
        $random_salt = hash('sha512', uniqid(openssl_random_pseudo_bytes(16), TRUE));
        // Erstelle saltet Passwort
        $password = hash('sha512', $password . $random_salt);
        // Trage den neuen Benutzer in die Datenbank ein
        if ($insert_stmt = $mysqli->prepare("INSERT INTO members (username, email, password, salt) VALUES (?, ?, ?, ?)")) {
            $insert_stmt->bind_param('ssss', $username, $email, $password, $random_salt);
            // Führe die vorbereitete Anfrage aus.
            if (! $insert_stmt->execute()) {
                header('Location: ../error.php?err=Registration failure: INSERT');
            }
        }
        header('Location: ./register_success.php');
    }
}

So, das klappt alles einwandfrei.

Änder ich jetzt aber die register.inc.php (keine Überprüfung ob "username" leer ist, keine Umwandlung von "username" und auch kein Eintrag in die DB), passiert aber gar nichts mehr und dennoch bekommt ich die Meldung "Registration successful".

Das sind dann so aus:
PHP:
<?php
include_once 'db_connect.php';
include_once 'psl-config.php';
$error_msg = "";
if (isset($_POST['email'], $_POST['p'])) {
    // Bereinige und überprüfe die Daten
    $email = filter_input(INPUT_POST, 'email', FILTER_SANITIZE_EMAIL);
    $email = filter_var($email, FILTER_VALIDATE_EMAIL);
    if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
        // keine gültige E-Mail
        $error_msg .= '<p class="error">The email address you entered is not valid</p>';
    }
    $password = filter_input(INPUT_POST, 'p', FILTER_SANITIZE_STRING);
    if (strlen($password) != 128) {
        // Das gehashte Passwort sollte 128 Zeichen lang sein.
        // Wenn nicht, dann ist etwas sehr seltsames passiert
        $error_msg .= '<p class="error">Invalid password configuration.</p>';
    }
    // Benutzername und Passwort wurde auf der Benutzer-Seite schon überprüft.
    // Das sollte genügen, denn niemand hat einen Vorteil, wenn diese Regeln 
    // verletzt werden.
    //
    $prep_stmt = "SELECT id FROM members WHERE email = ? LIMIT 1";
    $stmt = $mysqli->prepare($prep_stmt);
    if ($stmt) {
        $stmt->bind_param('s', $email);
        $stmt->execute();
        $stmt->store_result();
        if ($stmt->num_rows == 1) {
            // Ein Benutzer mit dieser E-Mail-Adresse existiert schon
            $error_msg .= '<p class="error">A user with this email address already exists.</p>';
        }
    } else {
        $error_msg .= '<p class="error">Database error</p>';
    }
    // Noch zu tun:
    // Wir müssen uns noch um den Fall kümmern, wo der Benutzer keine
    // Berechtigung für die Anmeldung hat indem wir überprüfen welche Art
    // von Benutzer versucht diese Operation durchzuführen.
    if (empty($error_msg)) {
        // Erstelle ein zufälliges Salt
        $random_salt = hash('sha512', uniqid(openssl_random_pseudo_bytes(16), TRUE));
        // Erstelle saltet Passwort
        $password = hash('sha512', $password . $random_salt);
        // Trage den neuen Benutzer in die Datenbank ein
        if ($insert_stmt = $mysqli->prepare("INSERT INTO members (email, password, salt) VALUES (?, ?, ?)")) {
            $insert_stmt->bind_param('ssss', $email, $password, $random_salt);
            // Führe die vorbereitete Anfrage aus.
            if (! $insert_stmt->execute()) {
                header('Location: ../error.php?err=Registration failure: INSERT');
            }
        }
        header('Location: ./register_success.php');
    }
}

Selbst wenn ich das Formular ändere (aktuell ist der Benutzername ja noch vorhanden) und den Benutzername weglassen, funktioniert es nicht.
 
Werbung:
Richtig debuggen

1. Man bemerkt, dass ein Skript nicht das tut, was es soll.
2. Man schreibt an den Anfang des Scriptes die Zeile: error_reporting(-1);
3. Man verwendet ini_set('display_errors', true); damit die Fehler auch angezeigt werden.
4. Man versucht, die Stelle die daran Schuld sein kann, schonmal einzugrenzen. Falls dies nicht geht, wird zunächst das komplette Skript als fehlerhaft angesehen.
5. An markanten Stellen im Skript lässt man sich wichtige Variableninhalte ausgeben und ggf. auch in bedingten Anweisungen eine kurze Ausgabe machen, um zu überprüfen, welche Bedingung ausgeführt wurde. Wichtig bei MySQL Fehlern (...not a valid MySQL result resource...): mysqli_error() verwenden oder Abfrage ausgeben und zb mit phpmyadmin testen.
6. Schritt 5 wird so lange wiederholt, bis Unstimmigkeiten im Skript auffallen
7. Damit hat man das Problem (Unstimmigkeit) gefunden und kann versuchen diese zu beheben. Hierzu dienen dann die PHP-Dokumentation und andere Quellen als Ratgeber.
8. Lässt sich das konkrete Problem trotzdem nicht beheben, kann man in Foren um Rat fragen.
9. Das Programm läuft und man kann die Debug-Ausgaben wieder entfernen.
 
Vielen Dank schon mal für die schnelle und ausführliche Antwort.
Problematisch ist ja leider, das es keine Fehler auswirft.
Es erfolgt einfach kein Insert, sonst passiert nichts (affected rows 0).
Leider weis ich gerade nicht, an welcher Stelle in die Fehlerabfrage einfügen müsste.

So klappt es jedenfalls nicht:
PHP:
 if ($insert_stmt = $mysqli->prepare("INSERT INTO members (email, password, salt) VALUES (?, ?, ?, ?)")) {
            $insert_stmt->bind_param('ssss', $email, $password, $random_salt);
            // Führe die vorbereitete Anfrage aus.
            if (! $insert_stmt->execute() ) {
                header('Location: ../error.php?err=Registration failure: INSERT');
            }
        }else{echo mysqli_error($mysqli);
        }

P.S. ich bin mir der kommenden virtuellen Ohrfeigen bewusst, leider weis ich gerade kein Rat.
 
Vielen Dank schon mal für die schnelle und ausführliche Antwort.
Problematisch ist ja leider, das es keine Fehler auswirft.
Es erfolgt einfach kein Insert, sonst passiert nichts (affected rows 0).
Leider weis ich gerade nicht, an welcher Stelle in die Fehlerabfrage einfügen müsste.

So klappt es jedenfalls nicht:
PHP:
 if ($insert_stmt = $mysqli->prepare("INSERT INTO members (email, password, salt) VALUES (?, ?, ?, ?)")) {
            $insert_stmt->bind_param('ssss', $email, $password, $random_salt);
            // Führe die vorbereitete Anfrage aus.
            if (! $insert_stmt->execute() ) {
                header('Location: ../error.php?err=Registration failure: INSERT');
            }
        }else{echo mysqli_error($mysqli);
        }

P.S. ich bin mir der kommenden virtuellen Ohrfeigen bewusst, leider weis ich gerade kein Rat.

Hier:
bind_param('ssss',$email,$password,$random_salt);

und hier:
INSERT INTO members (email, password, salt) VALUES (?, ?, ?, ?)
 
Werbung:
So einfach?
Den letzten Wert hatte ich auch schon mal entfernt, aber natürlich hatte ich das 4. s nicht gelöscht.

Dennoch wäre mir die Fehlerausgabe wichtig, damit ich zukünftig nicht ständig wegen jeden kleinen Fehler stundenlang suchen und rumrästeln (und ins Forum schreiben) muss. :)
 
So einfach?
Den letzten Wert hatte ich auch schon mal entfernt, aber natürlich hatte ich das 4. s nicht gelöscht.

Dennoch wäre mir die Fehlerausgabe wichtig, damit ich zukünftig nicht ständig wegen jeden kleinen Fehler stundenlang suchen und rumrästeln (und ins Forum schreiben) muss. :)

Da ich nur noch mit Doctrine arbeite, weiß ich es nicht mehr wirklich.
Aber 4x Typangabe bei 3 Parametern scheint mir unlogisch.

Die Fehlermeldung solltest du so bekommen:
PHP:
if (! $insert_stmt->execute() ) {
  header('Location: ../error.php?err='.$mysqli->error);
}
 
Zurück
Oben