Login funktioniert mit MD5-Verschlüsseluing nicht mehr
Onkel Schnitzel
- datenbank
Hallo,
ich hab mir kürzlich ein Login-System gebaut, das auch wunderbar funktioniert hat. Jetzt habe ich mich entschlossen, die Passwörter in MySQL4 lieber zu verschlüsseln. Seitdem schlagen alle Login-Versuche fehl, also auch wenn ich mich komplett als neuer Benutzer registriere, das Kennwort also mit MD5() in die Datenbank geschrieben wird. Ich bin jetzt nicht sicher, ob ich die richtige Syntax verwende, eine Fehlermeldung bekomme ich jedenfalls nicht. Vielleicht könnt Ihr ja mal gucken, ob euch was auffällt.
//Registrierung
$sql = "INSERT INTO login
(benutzer, pw)
VALUES
('".mysql_real_escape_string($_POST['benutzer'])."',
MD5('".mysql_real_escape_string($_POST['pw'])."'))";........
//Anmeldung
function login_ok($benutzer, $pw) {
$sql = "SELECT
COUNT(*) as Anzahl
FROM
login
WHERE
benutzer = '".$benutzer."' AND
pw = MD5('".$pw."') AND ..........
Also, wie gesagt, ohne MD5() funkioniert das Script.
Grüße,
Onkel Schnitzel
mysql_real_escape_string($_POST['pw']) und MD5('".$pw."') liefern nicht zwangsläufig den selben hash, da das escape zeichen auch gehashed wird wenn jemand zb ein hochkomma im password hat oder ein password wie zb 28\n$blah was auch immer
ggf fügbst du zum testen überall debugausgaben ein und vergleichst ob die variablen überall passen
Hello,
ob euch was auffällt.
auf den ersten Blick auf jeden Fall: Einmal werden Werte (korrekt) per mysql_real_escape_string maskiert, beim anderen Mal nicht.
MfG
Rouven
auf den ersten Blick auf jeden Fall: Einmal werden Werte (korrekt) per mysql_real_escape_string maskiert, beim anderen Mal nicht.
Naja, einmal werden ja Daten in die Datenbank hineingeschrieben, beim anderen Mal nur ausgelesen. Ich war der Meinung, dass mysql_real_escape_string dazu dient, die Datenbank zu schützen, indem gefährliche Zeichen maskiert werden. Beim Auslesen wäre das doch überflüssig oder liege ich da jetzt komplett daneben?
Gruß,
Onkel Schnitzel
Ok, ich hab mir gerade nochmal die anderen beiden Antworten zu Gemüte gezogen. Da lag ich wohl doch falsch. Ich werde das jetzt mal testen. Aber ich habe kein gutes Gefühl, denn das Passwort, mit dem ich getestet hatte, war nur eine einfache kurze Buchstabenfolge.
Grüße,
Schnitzel
Wie ich schon befürchtet hatte - es funktioniert nicht. Ich habe spaßenshalber mal die SQL-Funktion 'MD5' ersetzt durch 'CHAR_LENGTH'. Damit funktioniert die Registrierung/Anmeldung. Also liegt es wohl weniger an der Syntax, als vielmehr direkt an der MD5-Funktion. Hat irgendjemand noch eine Idee?
Grüße,
Onkel Schnitzel
Hallo,
Wie ich schon befürchtet hatte - es funktioniert nicht. [...] Hat irgendjemand noch eine Idee?
welchen Datentyp verwendest Du für diese Spalte?
Freundliche Grüße
Vinzenz
welchen Datentyp verwendest Du für diese Spalte?
Jaaa, das wars. Datentyp war VARCHAR(20), da hat der hash-Wert dummerweise nicht reingepasst :-/
Danke für den Denkanstoß :-)
Um nochmal zu der anderen Sache zu kommen. Ehrlich gesagt versteh ich nicht ganz, wie beim Auslesen aus einer Datenbank etwas injiziert werden kann. Gibt es dazu einen Artikel oder kanns jemand erklären?
Danke soweit...
Hallo,
welchen Datentyp verwendest Du für diese Spalte?
Jaaa, das wars. Datentyp war VARCHAR(20), da hat der hash-Wert dummerweise nicht reingepasst :-/
und Du hast auf BLOB umgestellt?
Freundliche Grüße
Vinzenz
und Du hast auf BLOB umgestellt?
nöööö?! Aber wenn du sagst, es ist besser...
Ich hab gerade gelesen, der Typ behandelt wohl den Inhalt als binären String, VARCHAR dagegen als zeichenbasierten String. Also kann ich davon ausgehen, dass BLOB geeigneter für solche Hash-Werte ist, oder wie?
Gruß,
Onkel Schnitzel
Hallo
und Du hast auf BLOB umgestellt?
nöööö?! Aber wenn du sagst, es ist besser...
Ich hab gerade gelesen, der Typ behandelt wohl den Inhalt als binären String, VARCHAR dagegen als zeichenbasierten String. Also kann ich davon ausgehen, dass BLOB geeigneter für solche Hash-Werte ist, oder wie?
ich dachte, Du kommst selbst auf die Idee, den entsprechenden Handbuchabschnitt zu lesen ...
Freundliche Grüße
Vinzenz
echo $begrüßung;
Ehrlich gesagt versteh ich nicht ganz, wie beim Auslesen aus einer Datenbank etwas injiziert werden kann. Gibt es dazu einen Artikel oder kanns jemand erklären?
Ein SQL-Statement, egal ob zum Eintragen oder zum Abfragen von Daten, ist ein Gemisch aus Anweisungsteilen und Daten. Genauso wie du unter PHP dieses Gemisch hast, wenn du einen Zeichenkette in den Quelltext schreiben willst. Damit nun der Emfänger Anweisung und Daten unterscheiden kann, werden die Daten in Anführungszeichen gesetzt. Das nennt man Quotieren. Wenn nun ein Anführungszeichen als Bestandteil der Daten auftaucht, muss dieses von den Begrenzungszeichen unterschieden werden. Unter SQL und PHP geschieht das durch Voranstellen eines . Das nennt man Maskieren (oder Escapen). Der \ wird nun ebenfalls ein Sonderzeichen und muss doppelt notiert werden. Für MySQL-Statements gibt noch einige weitere besonders zu notierende Zeichen. Um all diese kümmert sich die Funktion mysql_real_escape_string().
echo "$verabschiedung $name";
Um all diese kümmert sich die Funktion mysql_real_escape_string().
Ja, soweit hatte ich das auch verstanden. Nur, mein Stand war eben, dass die Funktion mysql_real_escape_string() nur beim Eintragen in die Datenbank benötigt wird. Wenn ich beispielsweise die Einträge in einem Gästebuch einfach nur auslese und diese bereits beim Eintragen maskiert wurden, warum sollte man das noch ein zweites Mal tun? Und da ja auch kein Formular vohanden ist, kann doch auch nichts 'injiziert' werden.
Gruß,
Onkel Schnitzel
Hallo,
Ja, soweit hatte ich das auch verstanden. Nur, mein Stand war eben, dass die Funktion mysql_real_escape_string() nur beim Eintragen in die Datenbank benötigt wird.
Du hast ein Formular, in das der Benutzer etwas eingeben kann.
Was passiert mit Deinem Skript, wenn der Benutzer jetzt als Namen
O'Brian
eingibt? Gern auch als Passwort. Was schickt Dein Skript an MySQL?
Freundliche Grüße
Vinzenz
Du hast ein Formular, in das der Benutzer etwas eingeben kann.
Was passiert mit Deinem Skript, wenn der Benutzer jetzt als NamenO'Brian
eingibt? Gern auch als Passwort. Was schickt Dein Skript an MySQL?
Es schickt O'Brian an MySQL, dort wird es "behandelt" und als O'Brian eingetragen. So steht es auch drin, wenn ich es mir über phpMyAdmin ansehe. Bei der Ausgabe in meinem Script wird der Name "gestripslashed" und ich lese O'Brian.
Gruß,
Onkel Schnitzel
Moin!
Du hast ein Formular, in das der Benutzer etwas eingeben kann.
Was passiert mit Deinem Skript, wenn der Benutzer jetzt als NamenO'Brian
eingibt? Gern auch als Passwort. Was schickt Dein Skript an MySQL?
Es schickt O'Brian an MySQL, dort wird es "behandelt" und als O'Brian eingetragen. So steht es auch drin, wenn ich es mir über phpMyAdmin ansehe. Bei der Ausgabe in meinem Script wird der Name "gestripslashed" und ich lese O'Brian.
Dann ist die nächste Frage: Warum ist das so, dass aus dem O'Brian aus dem Formulartextfeld ein O'Brian in der Datenbank wird?
Antwort: PHP hat die unangenehme Eigenschaft, eine Option zu besitzen (zum Glück abschaltbar), alle reinkommenden Formulardaten zu slashifizieren. magic_quotes_gpc genannt.
Irgendwer hat sich das mal ausgedacht, damit verhindert wird, dass User wie du sich ihre Datenbank schrotten, weil sie das SQL-Escaping weggelassen haben. Tolle Idee im Prinzip - wenn man die Daten denn immer in eine DB schreiben wollte. Will man Textdateien damit befüllen, stören die Slashes extrem. Außerdem decken die Slashes nicht zwingend alle Zeichen ab, auf die Datenbanken allergisch reagieren könnten, z.B. bei exotischeren Encodings.
Die Option ist also abschaltbar, es wird auch streng empfohlen, das zu tun, und künftige PHP-Versionen haben die Option gar nicht mehr. Dann endlich werden Scriptkiddies und Unwissende lernen müssen, was wir dir hier auch den Thread über beibringen wollen.
Zurück zur Ausgangslage: Angenommen, du hast einen String mit dem Namen O'Brian, den du in ein SQL-Statement einfügst. PHP hat dir diesmal KEIN Slash reingemogelt, dass du manuell (entweder vor der DB-Speicherung oder nach dem DB-Auslesen) entfernen mußt, weil diese Magic-Quotes-Option aus ist.
Wie sieht dein SQL-Statement aus, wenn du es einmal OHNE und einmal MIT mysql_real_escape_string() zusammenbastelst?
- Sven Rautenberg
Danke für Eure Erklärungen. Es war mir tatsächlich nicht bewusst, dass es sich bei mysql_real_escape_string() um eine Art "Transportsicherung" handelt. Ich war immer der Meinung, dass das direkt in die Datenbank übernommen wird. Da werde ich wohl das ein oder andere Script nochmal überarbeiten müssen.
Das mit den magiq quotes wusste ich eigentlich schonmal, nur ist es mir wohl zwischenzeitlich wieder entfallen, ist halt schon wieder eine Weile her, dass ich mich damit beschäftigt habe. Momentan habe ich ehrlich gesagt keine Muße, das zu ändern, weil alles so schön läuft. Ich denke, ich werde es demnächt aber mal vorsichtig antesten.
Zurück zur Ausgangslage: Angenommen, du hast einen String mit dem Namen O'Brian, den du in ein SQL-Statement einfügst. PHP hat dir diesmal KEIN Slash reingemogelt, dass du manuell (entweder vor der DB-Speicherung oder nach dem DB-Auslesen) entfernen mußt, weil diese Magic-Quotes-Option aus ist.
Wie sieht dein SQL-Statement aus, wenn du es einmal OHNE und einmal MIT mysql_real_escape_string() zusammenbastelst?
Da erscheinen so einige Strichelchen. Mit mysql_real_escape_string() wird " O\'Brian " daraus, ohne die Funktion nur " O'Brian ". Der magic-quotes-Slash wird mit mysql_real_escape_string() seinerseits also anscheinend noch einmal maskiert. In der Datenbank erscheint aber nur O'Brian. Also wirkt die Funktion wohl wirklich nicht direkt auf die Datenbank, wolltest du darauf hinaus? Ich denke mal, dass ich das Gröbste jetzt verstanden habe.
Danke nochmal an Euch alle!
echo $begrüßung;
Du hast ein Formular, in das der Benutzer etwas eingeben kann.
Was passiert mit Deinem Skript, wenn der Benutzer jetzt als Namen
O'Brian
eingibt? Gern auch als Passwort. Was schickt Dein Skript an MySQL?
Es schickt O'Brian an MySQL, dort wird es "behandelt" und als O'Brian eingetragen. So steht es auch drin, wenn ich es mir über phpMyAdmin ansehe. Bei der Ausgabe in meinem Script wird der Name "gestripslashed" und ich lese O'Brian.
Dann hat dir anscheinend ein Feature von PHP einen Streich gespielt: Magic Quotes. Seine Aufgabe ist es, ebensolche SQL-Injektion zu verhindern, indem es die Maskierungen vornimmt. Doch dieses Feature wirkt auf jegliche Eingabedaten. Und von denen ist beim Scriptstart noch gar nicht bekannt, ob sie in einem SQL-Statement zu stehen kommen oder in einem ganz anderen Kontext mit komplett anderen Regeln ausgegeben werden. Nicht nur weil es fehlplatziert ist, ist es nicht mehr länger geduldet und fällt ab PHP6 weg.
Du wendest dann mysql_real_escape_string() auf die bereits behandelten Daten an, und maskierst ungefähr doppelt. "Ungefähr" deshalb, weil Magic Quotes weniger Zeichen behandelt als mysql_real_escape_string(). Ersteres ist sehr allgemein gehalten, letzeres auf MySQL-Statements spezialisiert. Durch die doppelte Behandlung hast du nun unnötige Zeichen in der Datenbank stehen. Die eine Maskierung wird als Transportsicherung beim Auswerten des Statements wieder entfernt, die andere ist zu nichts nütze außer zum Verfälschen von Stringfunktionsergebnissen, die MySQL gegebenenfalls auf die Daten ausführen soll. Z.B. ist die Länge von foo'bar eine andere als die von foo'bar. foo'bar passt auch nicht in ein Feld für 7 Zeichen.
Es schickt O'Brian an MySQL, dort wird es "behandelt" und als O'Brian eingetragen.
Das ist nicht richtig. Mach Kontrollausgaben der Werte (am besten mit var_dump(), denn das gibt dir unter anderem auch die Anzahl der Zeichen eines Strings aus), so wie sie dein Script erreichen, und so wie das fertige SQL-Statement aussieht, bevor du es mysql_query() übergibst. MySQL "behandelt", ja, aber nicht so wie du dir das vorstellst. Es entfernt die Transportsicherung und reduziert deine doppelte Maskierung zu einer einfachen.
So steht es auch drin, wenn ich es mir über phpMyAdmin ansehe.
Du siehst die eine übrig gebliebene und überflüssige Maskierung.
Bei der Ausgabe in meinem Script wird der Name "gestripslashed" und ich lese O'Brian.
Das kannst du dir sparen, wenn du
Somit hast du nur eine Maskierung für den Transport der Daten in einem SQL-Statement zum MySQL-Server. Da die Daten auf dem Rückweg aus dem MySQL-Server separat und nicht gemixt mit Anweisungsteilen transportiert werden, ist eine Transportsicherung nicht notwendig. Du bekommst also die Daten in Rohform von den mysql_fetch_*()-Funktionen geliefert.
echo "$verabschiedung $name";
echo $begrüßung;
Um all diese kümmert sich die Funktion mysql_real_escape_string().
Ja, soweit hatte ich das auch verstanden.
Mir scheint nicht, dass du meine Ausführungen in Gänze verstanden hast.
Nur, mein Stand war eben, dass die Funktion mysql_real_escape_string() nur beim Eintragen in die Datenbank benötigt wird.
Die Injektion bezieht sich nicht auf die Datenbank sondern auf das SQL-Statement. Und dem ist es egal, ob es nun beim Schreiben ungewünschte Dinge anstellt oder beim Lesen unerwünscht Daten rausrückt.
Wenn ich beispielsweise die Einträge in einem Gästebuch einfach nur auslese und diese bereits beim Eintragen maskiert wurden, warum sollte man das noch ein zweites Mal tun?
Diese Behandlung wird nicht für den Wert in dem Tabellenfeld durchgeführt sondern für das SQL-Statement, das hier als gemeinsames Transportmedium von Anweisung und Daten auftritt. Wenn der MySQL-Server dieses Statement auswertet, dann entfernt er diese Transportsicherung wieder, wenn er die Anweisungsteile von den Daten trennt. In den Feldern ist von der Maskierung nichts mehr vorhanden.
Und da ja auch kein Formular vohanden ist, kann doch auch nichts 'injiziert' werden.
Dem SQL-Statement ist es egal, woher es seine Daten bekommt. Es ist nicht allein der Sicherheitsaspekt, den du beachten musst, sondern allgemein betrachtet ein Kontext, in dem bestimmte Anforderungen an die Notation der Daten gelten. Ein ' in einem mit ' begrenztem String ist nun mal als ' zu notieren. Es spielt keine Rolle, ob da jemand was absichtlich ausnutzen will und dein SQL-Statement verfälscht
SELECT * FROM user WHERE name='administrator' AND password='' OR 1 --'
oder per Zufall dieses Zeichen auftaucht und nur einen Syntax-Fehler hervorruft
SELECT * FROM user WHERE name='irgendwer' AND password='hab's mit Sonderzeichen versehen'
echo "$verabschiedung $name";
Moin
Trotz alledem ist das escapen an dieser Stelle wichtig. Der Tipp war also nicht umsonst.
Gruß Bobby
Moin
//Anmeldung
function login_ok($benutzer, $pw) {
$sql = "SELECT
COUNT(*) as Anzahl
FROM
login
WHERE
benutzer = '".$benutzer."' AND
pw = MD5('".$pw."') AND ..........
Die Abfrage muss ebenfalls in die Funktion mysql_real_escape_string gepackt werden, da ja Sonderzeichen im PW auftauchen können und diese natürlich von der Funktion beim speichern escaped wurden.
Also sollte funktionieren:
//Anmeldung
function login_ok($benutzer, $pw) {
$sql = "SELECT
COUNT(*) as Anzahl
FROM
login
WHERE
benutzer = '".mysql_real_escape_string($benutzer)."' AND
pw = MD5('".mysql_real_escape_string($pw)."') AND ..........
Als Hinweis noch. Man muß auch bei einer Abfrage jeden String escapen, da auch bei der Abfrage MySQL-Injection mittels eingegebenen String im Formular geschehen kann. Stichwort: UNION
Gruß Bobby