Florian: MySQL:Userdaten werden nicht korrekt ausgelesen

Hi,
ich habe einen Login in PHP geschrieben, der die Userdaten und Passwörter aus einer MySQL-Datenbank herausliest.
Aber irgendwie funktioniert es nicht so wie es soll.
Die Userdaten werden nicht korrekt ausgelesen, momentan wird gemeldet, dass der Username nicht existiert, egal ob er in PHPMyAdmin eingetragen wurde oder nicht.

Die Datenbank setzt sich so zusammen:

datenbank---Tabelle user   ---Spalten id,date,nick,message
         ---Tabelle message---Spalten nick,pass,online

(Reihenfolge wie hier angegeben).

Der Quellcode meiner login.php (mit POST wurde $_POST['nick'] und $_POST['pass'] übermittelt)

-----------------------------------------------------------------
<?php
$db_user=@mysql_connect("localhost","username","passwort") or die ("Verbindung zu Mysql gescheitert!");
@mysql_select_db("datenbank",$db_user) or die ("Datenbankzugriff gescheitert!");//Verbindung zur Tabelle User
$nick=$_POST['nick'];  //Eingegebener Nickname wird abgefangen
$pass=$_POST['pass'];  //Eingegebenes pass wird abgefangen
if ($nick=="Gast" || $nick=""){        //Wenn kein Name/Gast eingegeben wurde
 $nick="Gast";
 $pass="";
}                                      //Kein Name/Gast ENDE

$row=mysql_fetch_object(mysql_query("SELECT * FROM user WHERE nick='".$nick."'"));
$pass_sql=$row->pass;

if ($pass_sql==$pass) {    //Wenn pass richtig, dann Kekse setzen
setcookie( "nick", $nick);
setcookie( "pass", $pass);
//und Ausgabe der html-datei
echo <<<AUSGABE
<html>
<head>
<meta name="author" content="Florian Klink">
<link href="/css/sv.css" rel="stylesheet" type="text/css">
<link href="/scripts/navi/navi.css" rel="stylesheet" type="text/css">
<title>diesv.de:: Chat</title></head>
<body>
 <script type="text/javascript">
 document.location="chat.php";
 alert ('Anmeldung erfolgreich! \nDu wirst weitergeleitet.');
 </script>
</body>
</html>

AUSGABE;
}
else{ //Wenn pass falsch, dann Ausgabe pass Falsch und zurückleitung auf die login-seite
echo <<<AUSGABE
<html>
<head>
<meta name="author" content="Florian Klink">
<link href="/css/sv.css" rel="stylesheet" type="text/css">
<link href="/scripts/navi/navi.css" rel="stylesheet" type="text/css">
<title>diesv.de:: Chat</title></head>
<body>
<script type="text/javascript">
alert ('Passwort oder Nick falsch.\nBitte gebe die Daten erneut ein.');
document.location="index.php";
</script>
</body>
</html>
AUSGABE;
}
?>

Wo liegt mein Fehler?

Florian Klink

  1. Hallo Florian,

    ich habe einen Login in PHP geschrieben, der die Userdaten und Passwörter aus einer MySQL-Datenbank herausliest.

    $row=mysql_fetch_object(mysql_query("SELECT * FROM user WHERE nick='".$nick."'"));

    Du übernimmst den per POST gesendeten Wert direkt? Was wäre, wenn jemand für $_POST['nick']
    meinnickname'; DELETE * FROM users;
    eingibt? Dann wird das Query zu
    SELECT * FROM user WHERE nick='meinnickname'; DELETE * FROM users;
    und die ganze Tabelle wird gelöscht (zum Beispiel, da geht noch einiges mehr).
    Link des Tages: mysql_escape_string()

    if ($pass_sql==$pass) {    //Wenn pass richtig, dann Kekse setzen
    setcookie( "nick", $nick);
    setcookie( "pass", $pass);

    Du speicherst das Passwort direkt, unverschlüsselt als Cookie? Das kann dann jeder lesen!
    Link des Tages: md5()

    //und Ausgabe der html-datei
    echo <<<AUSGABE
    <html>

    Und wo ist die Doctype Definition?

    <script type="text/javascript">
    document.location="chat.php";
    alert ('Anmeldung erfolgreich! \nDu wirst weitergeleitet.');

    Und wenn jemand kein Javascript aktiviert hat? Dann wird er weder weitergeleitet noch sieht er die Meldung.
    Link des Tages: Meta Refresh
    (wahrscheinlich wird das alert nie funktionieren, weil nix mehr gemacht wird, wenn du schon die Seite gewechselt hast.

    <html>

    Und wo... Ich wiederhole mich.

    alert ('Passwort oder Nick falsch.\nBitte gebe die Daten erneut ein.');
    document.location="index.php";

    Und wenn... Ich wiederhole mich.
    Hier hingegen wird, wenn Javascript aktiviert ist, das alert auch angezeigt.

    Wo liegt mein Fehler?

    Lass dir mal das erzeugte Query ausgeben, dann siehst du, wenn irgendwas nicht stimmt.

    Viele Grüße aus Freiburg,
    Marian

    --
    Mein SelfCode: ie:{ fl:( br:> va:) ls:[ fo:) n4:( ss:) de:] js:| ch:? mo:} zu:)
    <!--[if IE]><meta http-equiv="refresh" content="0; URL=http://www.getfirefox.com"><[endif]-->
    1. echo $begrüßung;

      $row=mysql_fetch_object(mysql_query("SELECT * FROM user WHERE nick='".$nick."'"));

      Du übernimmst den per POST gesendeten Wert direkt? Was wäre, wenn jemand für $_POST['nick']
      meinnickname'; DELETE * FROM users;
      eingibt? Dann wird das Query zu
      SELECT * FROM user WHERE nick='meinnickname'; DELETE * FROM users;
      und die ganze Tabelle wird gelöscht (zum Beispiel, da geht noch einiges mehr).

      Nein, da geht nichts mehr. Schon seit einiger Zeit ist es nicht mehr möglich, mehrere Befehle mit einem mysql_query()-Aufruf abzusetzen. Dazu braucht man PHP5, mysqli und die Funktion mysqli_multi_query(). Natürlich kann man immer noch das eine Statement so umschreiben, dass was anderes als das Gewünschte ausgeführt wird. Außerdem muss magic_quotes ausgeschaltet sein, sonst sind die Angriffsmöglichkeiten auch schon etwas eingeschränkter. Magic Quotes wirken sich aber nicht nur bei Datenbankbefehlen prositiv aus, sie sind an anderen Stellen kontraproduktiv, beispielsweise da, wo die Eingabe auf dem Bildschirm landen soll. Deswegen ist es empfehlenswert, die Magic Quotes generell auszuschalten, oder die eingefügten Maskierungen am Scriptanfang zu entfernen, die eingegebenen Daten auf Plausibilität zu prüfen und erst direkt vor der Übergabe an das Ausgabemedium entsprechend zu maskieren.

      Link des Tages: mysql_escape_string()

      Für MySQL nimmt man dafür die Funktion mysql_real_escape_string(). Das von dir vorgeschlagene mysql_escape_string() ist veraltet und soll nicht mehr genommen werden.

      if ($pass_sql==$pass) {    //Wenn pass richtig, dann Kekse setzen
      setcookie( "nick", $nick);
      setcookie( "pass", $pass);

      Du speicherst das Passwort direkt, unverschlüsselt als Cookie? Das kann dann jeder lesen!
      Link des Tages: md5()

      Der "Link des Tages" ist schon verbraucht. Was willst du mit dem md5() anstellen? Da fehlt doch noch das passende Gegenstück in der Datenbank dazu. Wo ist außerdem der Unterschied, ob man ein Passwort mit einem Passwort oder einen MD5-String mit einem MD5-String vergleicht? Es wäre erst einmal zu klären, wozu Nick und Passwort aufgehoben werden. Dann kann man ein dafür passenderes Konzept vorschlagen.

      Und wenn jemand kein Javascript aktiviert hat? Dann wird er weder weitergeleitet noch sieht er die Meldung.
      Link des Tages: Meta Refresh

      Der "Link des Tages" ist schon verbraucht. Das Ausführen von Meta-Refresh kann man dem Browser auch verbieten. Außerdem wird dadurch der Back-Button-Mechanismus kaputt gemacht. Und warum soll man den Ersatz (http-equiv=HTTP-Äquivalent) nehmen, wenn dank PHP das Original in Form eines "echten" HTTP-Mechanismus vorhanden ist, so solch eine Weiterleitung unbedingt erforderlich ist: header('Location: vollständige URL');

      echo "$verabschiedung $name";

      1. Hallo dedlfix,

        Du übernimmst den per POST gesendeten Wert direkt? Was wäre, wenn jemand für $_POST['nick']
        meinnickname'; DELETE * FROM users;
        eingibt? Dann wird das Query zu
        SELECT * FROM user WHERE nick='meinnickname'; DELETE * FROM users;
        und die ganze Tabelle wird gelöscht (zum Beispiel, da geht noch einiges mehr).

        Nein, da geht nichts mehr. Schon seit einiger Zeit ist es nicht mehr möglich, mehrere Befehle mit einem mysql_query()-Aufruf abzusetzen.

        Ich erinnere mich...

        Link des Tages: mysql_escape_string()

        Für MySQL nimmt man dafür die Funktion mysql_real_escape_string(). Das von dir vorgeschlagene mysql_escape_string() ist veraltet und soll nicht mehr genommen werden.

        Wusst ich noch garnicht. Danke für den Hinweis.

        if ($pass_sql==$pass) {    //Wenn pass richtig, dann Kekse setzen
        setcookie( "nick", $nick);
        setcookie( "pass", $pass);

        Du speicherst das Passwort direkt, unverschlüsselt als Cookie? Das kann dann jeder lesen!
        Link des Tages: md5()

        Der "Link des Tages" ist schon verbraucht.

        Der is für morgen (bzw schon heute) ;-)

        Was willst du mit dem md5() anstellen? Da fehlt doch noch das passende Gegenstück in der Datenbank dazu. Wo ist außerdem der Unterschied, ob man ein Passwort mit einem Passwort oder einen MD5-String mit einem MD5-String vergleicht? Es wäre erst einmal zu klären, wozu Nick und Passwort aufgehoben werden. Dann kann man ein dafür passenderes Konzept vorschlagen.

        Auf jeden Fall sollte (eher darf) das Passwort nicht unverschlüsselt per Cookie gespeichert werden. Und man sollte es auch in der Datenbank nur verschlüsselt speichern. (Dann gibt es auch keine Probleme beim Vergleichen)

        Und wenn jemand kein Javascript aktiviert hat? Dann wird er weder weitergeleitet noch sieht er die Meldung.
        Link des Tages: Meta Refresh

        Der "Link des Tages" ist schon verbraucht. Das Ausführen von Meta-Refresh kann man dem Browser auch verbieten. Außerdem wird dadurch der Back-Button-Mechanismus kaputt gemacht. Und warum soll man den Ersatz (http-equiv=HTTP-Äquivalent) nehmen, wenn dank PHP das Original in Form eines "echten" HTTP-Mechanismus vorhanden ist, so solch eine Weiterleitung unbedingt erforderlich ist: header('Location: vollständige URL');

        Dann kann er aber keine Meldung anzeigen. Wenn er Meta-Refresh z.B. auf 10 Sekunden setzt und (zumindest auch mit HTML) "Sie sind angemeldet" hinschreibt und daneben einen Link "Weiter...", geht das ohne Probleme.

        Viele Grüße aus Freiburg,
        Marian

        --
        Mein SelfCode: ie:{ fl:( br:> va:) ls:[ fo:) n4:( ss:) de:] js:| ch:? mo:} zu:)
        <!--[if IE]><meta http-equiv="refresh" content="0; URL=http://www.getfirefox.com"><[endif]-->
        1. echo $begrüßung;

          [HTTP-Header statt HTML-META-Element]

          Dann kann er aber keine Meldung anzeigen. Wenn er Meta-Refresh z.B. auf 10 Sekunden setzt und (zumindest auch mit HTML) "Sie sind angemeldet" hinschreibt und daneben einen Link "Weiter...", geht das ohne Probleme.

          Ist diese Information überhaupt nötig, bzw. in der Form nötig? Sieht man das nicht daran, dass man nun die Funktionen zur Verfügung hat, die der eingeloggten Kennung erlaubt sind? Und reicht es nicht bei Misserfolg eine Meldung inklusive neuem Anmeldeformular anzuzeigen?

          echo "$verabschiedung $name";

          1. Hallo dedlfix,

            Ist diese Information überhaupt nötig, bzw. in der Form nötig? Sieht man das nicht daran, dass man nun die Funktionen zur Verfügung hat, die der eingeloggten Kennung erlaubt sind? Und reicht es nicht bei Misserfolg eine Meldung inklusive neuem Anmeldeformular anzuzeigen?

            Natürlich. Ich hab ihm halt gezeigt, wie es auch ohne Javascript geht.

            Viele Grüße aus Freiburg,
            Marian

            --
            Mein SelfCode: ie:{ fl:( br:> va:) ls:[ fo:) n4:( ss:) de:] js:| ch:? mo:} zu:)
            <!--[if IE]><meta http-equiv="refresh" content="0; URL=http://www.getfirefox.com"><[endif]-->
            1. Ich hab jetzt den Code geändert, der Pass-Cookie wird nicht mehr gesetzt.
              Zugangsdaten sind jetzt ausgelagert.
              Allerdings wird jetzt IMMER gemeldet, dass das Passwort falsch ist, auch wenn es richtig ist (nur der else greift)
              Die sql-abfrage (scheint) jetzt halb zu laufen, aber wie gesagt...richtige Passwörter werden nicht erkannt.

              <?php
              include("connect_db_user.php");
              $nick=$_POST['nick'];  //Eingegebener Nickname wird abgefangen
              $pass=$_POST['pass'];  //Eingegebenes pass wird abgefangen
              if ($nick=="Gast" || $nick=""){        //Wenn kein Name/Gast eingegeben wurde
               $nick="Gast";
               $pass="";
              }                                      //Kein Name/Gast ENDE

              $sql="SELECT * FROM user WHERE nick='".$nick."'";
              $row=mysql_fetch_object(mysql_query($sql));
              $pass_sql=$row->pass;

              if ($pass_sql==$pass) {    //Wenn pass richtig, dann Keks setzen
              setcookie( "nick", $nick);
              ?>
              <html>
              <head>
              <meta name="author" content="Florian Klink">
              <link href="/css/sv.css" rel="stylesheet" type="text/css">
              <link href="/scripts/navi/navi.css" rel="stylesheet" type="text/css">
              <title>diesv.de:: Chat</title></head>
              <body>
               <script type="text/javascript">
               document.location="chat.php";
               </script>
              </body>
              </html>
              <?php
              }
              else{ //Wenn pass falsch, dann Ausgabe pass Falsch und zurückleitung auf die login-seite
              ?>
              <html>
              <head>
              <meta name="author" content="Florian Klink">
              <link href="/css/sv.css" rel="stylesheet" type="text/css">
              <link href="/scripts/navi/navi.css" rel="stylesheet" type="text/css">
              <title>diesv.de:: Chat</title></head>
              <body>
              <script type="text/javascript">
              alert ('Nickname oder Passwort falsch. \nBitte gib die Daten erneut ein.');
              document.location="index.php";
              </script>
              </body>
              </html>
              <?php
              }
              ?>

              1. echo $begrüßung;

                $sql="SELECT * FROM user WHERE nick='".$nick."'";
                $row=mysql_fetch_object(mysql_query($sql));

                Du ignorierst immer noch Fehlermeldungen von MySQL.

                Allerdings wird jetzt IMMER gemeldet, dass das Passwort falsch ist, auch wenn es richtig ist

                if ($pass_sql==$pass) {    //Wenn pass richtig, dann Keks setzen

                Was ergab deine Überprüfung der Werte von $pass und $pass_sql? Hast du sie dir mal mit echo oder noch besser mit var_dump() ausgeben lassen?

                echo "$verabschiedung $name";

                1. Und wie ich schon einmal gesagt habe, scheitert nicht die Verbindung, sondern die query

                  1. echo $begrüßung;

                    Und wie ich schon einmal gesagt habe, scheitert nicht die Verbindung, sondern die query

                    Ja doch. Fehler können nicht nur beim Verbindungsaufbau auftreten sondern auch beim Abarbeiten der Query. Also frage auch dort die Fehlermeldung ab, wenn dir die Funktion mysql_query() durch Rückgabe von false mitteilt, dass ein Fehler vorliegt.

                    Und schaue dir bitte mit var_dump($variablenname) deine Variableninhalte an, damit du einen Anhaltspunkt bekommst, warum die Abfrage nicht funktioniert. Alles andere ist Glaskugelraten und daran kann ich mich mangels Können nicht beteiligen.

                    echo "$verabschiedung $name";

                    1. Also, jetzt geht der login, es lag an der syntax des querys

                  2. Hi Florian,

                    Und wie ich schon einmal gesagt habe, scheitert nicht die Verbindung, sondern die query

                    Für Queries kannst du ganz genauso mit mysql_error() Fehlermeldungen ausgeben lassen, wie für die Verbindung, aber das hätte dir natürlich auch das PHP Manual gesagt.

                    $result = mysql_query($sql) OR die(mysql_error());

                    Ist die Kurzform, geht natürlich auch etwas schöner und vor allem, ohne das Script sterben zu lassen - mysql_errno() mag hilfreich sein.

                    if(mysql_errno() !== 0) {  
                      echo "<b>Line " . __LINE__ . ":</b> ";  
                      echo "MySQL Error - " . mysql_error();  
                    }
                    

                    MfG, Dennis.

        2. Hi Marian,

          Auf jeden Fall sollte (eher darf) das Passwort nicht unverschlüsselt per Cookie gespeichert werden. Und man sollte es auch in der Datenbank nur verschlüsselt speichern. (Dann gibt es auch keine Probleme beim Vergleichen)

          Eigentlich sollte man überhaupt keine Passwörter - egal welche form - beim Clienten speichern, die Daten könnten nämlich von einer anderen Person an dem PC ausgelesen werden. Dann würde ich Username und MD5-String des Passwortes wissen, die Daten bei mir am PC als Cookie setzen und könnte mich herlich einloggen.

          OK - einen Vorteil hat MD5 natürlich, ich kenne nicht das wirklich Passwort und habe somit keinen Angriffspunkt für andere Benutzerkonten der Person, wo vielleicht dasselbe Passwort verwendet wird.

          MfG, Dennis.

          1. Hallo Dennis,

            Auf jeden Fall sollte (eher darf) das Passwort nicht unverschlüsselt per Cookie gespeichert werden. Und man sollte es auch in der Datenbank nur verschlüsselt speichern. (Dann gibt es auch keine Probleme beim Vergleichen)

            Eigentlich sollte man überhaupt keine Passwörter - egal welche form - beim Clienten speichern, die Daten könnten nämlich von einer anderen Person an dem PC ausgelesen werden. Dann würde ich Username und MD5-String des Passwortes wissen, die Daten bei mir am PC als Cookie setzen und könnte mich herlich einloggen.

            Das geht (fast) immer. Irgendwas muss ja immer übertragen werden.
            Da hilft nur eine verschlüsselte Verbindung oder die Session auf eine bestimmte IP zu beschränken - aber die kann sich auch mal ändern.

            Es ist auf jeden Fall besser, als es unverschlüsselt zu speichern.

            Viele Grüße aus Freiburg,
            Marian

            --
            Mein SelfCode: ie:{ fl:( br:> va:) ls:[ fo:) n4:( ss:) de:] js:| ch:? mo:} zu:)
            <!--[if IE]><meta http-equiv="refresh" content="0; URL=http://www.getfirefox.com"><[endif]-->
  2. echo $begrüßung;

    $db_user=@mysql_connect("localhost","username","passwort") or die ("Verbindung zu Mysql gescheitert!");

    Willst du gar nicht wissen, warum die Verbindung gescheitert ist? Oder willst du nur nicht die genaue Fehlermeldung dem Besucher mitteilen, weil der
    a) damit sowieso nichts anfangen kann und
    b) keine Systeminterna erfahren soll, die er im Zweifelsfall gegen dein System verwenden kann?
    Wenn du die beiden Fragen mit "doch" und mit "ja" beantwortest, vermisse ich eine Log-Möglichkeit. Außerdem sieht das Sterbenlassen eines Scripts alles andere als schön aus.

    @mysql_select_db("datenbank",$db_user) or die ("Datenbankzugriff gescheitert!");//Verbindung zur Tabelle User

    s.o.

    $row=mysql_fetch_object(mysql_query("SELECT * FROM user WHERE nick='".$nick."'"));

    Du prüfst nicht, ob mysql_query() einen für mysql_fetch_object() geeigneten Rückgabewert zurückgibt. Im Fehlerfall gibt mysql_query() false zurück. Dies ist aber kein gültiger Parameter für mysql_fetch_object(). Dass du den Fehlermeldungstext von MySQL unbeachtest lässt, hab ich ja oben schon gesagt.

    Das Verschachteln der Befehle ist also mindestens ungünstig. Wie es besser geht zeigt das Beispiel im Kapitel MySQL im PHP-Handbuch.

    $pass_sql=$row->pass;

    Du prüfst nicht, ob $row überhaupt das enthält, was du erwartest und greifst dann munter darauf zu.

    alert ('Passwort oder Nick falsch.\nBitte gebe die Daten erneut ein.');

    (Der Imperativ von geben lautet in der zweiten Person gib.)

    Wo liegt mein Fehler?

    Das hat dir bestimmt das MySQL gesagt, du hast es aber ignoriert: mysql_error()

    echo "$verabschiedung $name";