Donald2603: MYSQL mysqli_real_escape_string

Hallo Leute,

Ich lese schon den ganzen Tag über Sicherheit und SQL-Injektionen. Bin neu in dem Thema SQL.
Derzeit sieht mein Code in etwa so aus:

  
$db = mysqli_connect('xxx', 'xxx', 'xxx', 'xxx') or exit;  
if(!empty($_POST['input1']))  
{  
    $benutzer = htmlspecialchars($_POST['input1']);  
}  
$input1sql = mysqli_real_escape_string($db, $benutzer);  
$query = mysqli_query($db, 'SELECT benutzer FROM users WHERE benutzer=\''.$input1sql.'\'');  

Zusätzlich dazu habe ich folgende Codezeile eingegeben, um zu testen, was mysqli_real_escape_string verändert:

echo $benutzer . '<br />' . $input1sql;

Jedoch zeigen beide Variablen das Gleiche ein, egal was ich eingebe. Ich habe es mit der Eingabe: testnutzer" OR 1 OR " versucht. Der echo ergab zwei identische Strings. Wie sicher ist der Code?

Derzeit habe ich noch nicht durchblickt, wie prepared Statements funktionieren. Bringt das deutlich mehr Sicherheit für die Abfrage?

Vielen Dank euch und beste Grüße

Donald

  1. Tach!

    $db = mysqli_connect('xxx', 'xxx', 'xxx', 'xxx') or exit;

    Findest du es für Benutzer sonnvoll, bei einem Datenbankfehler die Arbeit sang- und klanglos einzustellen?

    if(!empty($_POST['input1']))
    {
        $benutzer = htmlspecialchars($_POST['input1']);
    }

    Hast du hier einen HTML-Kontext vorliegen? Ich meine jetzt, nicht erst irgendwann später. Sieht nicht so aus, also ist an der Stelle das htmlspecialchars() falsch.

    $input1sql = mysqli_real_escape_string($db, $benutzer);
    Zusätzlich dazu habe ich folgende Codezeile eingegeben, um zu testen, was mysqli_real_escape_string verändert:
    echo $benutzer . '<br />' . $input1sql;
    Jedoch zeigen beide Variablen das Gleiche ein, egal was ich eingebe. Ich habe es mit der Eingabe: testnutzer" OR 1 OR " versucht. Der echo ergab zwei identische Strings. Wie sicher ist der Code?

    Nun, dein fehlerhaftes htmlspecialchars() hat die " zu &quot; verwurstet. &quot; enthält kein Zeichen, das von mysqli_real_escape_string() verändert wird. Du schaust dir beides im Browser an, und da kein HTML-Element geöffnet ist, in dem " eine Bedeutung hätte, zeigt der die " an, und aus dem &quot; macht er auch wieder ein ". Damit sieht beides gleich aus. Ein Blick in die Quelltextansicht oder die Ausgabe mit var_dump() hätte Unterschiede aufgezeigt. Bei var_dump() hätte die unterschiedliche Stringlänge auffallen müssen, auch wenn das Anzeigeergebnis dasselbe gewesen wäre, weil wieder das &quot; vom Browser weginterpretiert wird.

    Derzeit habe ich noch nicht durchblickt, wie prepared Statements funktionieren. Bringt das deutlich mehr Sicherheit für die Abfrage?

    Damit kann aus Prinzip keine SQL-Injection erfolgen, weil Code und Daten getrennte Wege gehen - wenn du sie konsequent einsetzt und nicht an ihnen vorbei variable Codeteile in die Query einsetzt.

    dedlfix.

    1. Findest du es für Benutzer sonnvoll, bei einem Datenbankfehler die Arbeit sang- und klanglos einzustellen?

      Ja, weil sonst eh nix läuft. Ob ich nun eine Fehlermeldung ausspucke oder einfach nix kommt. Wenn nix kommt, wird der Nutzer eher F5 drücken.

      if(!empty($_POST['input1']))
      {
          $benutzer = htmlspecialchars($_POST['input1']);
      }

      Hast du hier einen HTML-Kontext vorliegen? Ich meine jetzt, nicht erst irgendwann später. Sieht nicht so aus, also ist an der Stelle das htmlspecialchars() falsch.

      Ich gebe den Benutzer später aus über Hallo $benutzer. Ich escape generell alle Inputs über das Teil, damit keiner HTML-Code einschleust.
      Dafür bearbeite ich die Rückgaben von der DB gar nicht, weil ich davon ausgehe, dass die bereits entsprechend bearbeitet sind.

      Nun, dein fehlerhaftes htmlspecialchars() hat die " zu &quot; verwurstet. &quot; enthält kein Zeichen, das von mysqli_real_escape_string() verändert wird. Du schaust dir beides im Browser an, und da kein HTML-Element geöffnet ist, in dem " eine Bedeutung hätte, zeigt der die " an, und aus dem &quot; macht er auch wieder ein ". Damit sieht beides gleich aus. Ein Blick in die Quelltextansicht oder die Ausgabe mit var_dump() hätte Unterschiede aufgezeigt. Bei var_dump() hätte die unterschiedliche Stringlänge auffallen müssen, auch wenn das Anzeigeergebnis dasselbe gewesen wäre, weil wieder das &quot; vom Browser weginterpretiert wird.

      Das heißt mit dem htmlspecialchars() habe ich den String bereits entschärft. Ich habe es gerade mal mit ' OR 1 OR ' probiert. Der wird nun von mysqli_real_escape_string entschärft. Ob ich nun " oder &qout; in der DB speichere ist jetzt auch egal. Das Ergebnisse ist für den Nutzer das Gleiche.

      Donald

      1. Tach!

        Findest du es für Benutzer sonnvoll, bei einem Datenbankfehler die Arbeit sang- und klanglos einzustellen?
        Ja, weil sonst eh nix läuft. Ob ich nun eine Fehlermeldung ausspucke oder einfach nix kommt. Wenn nix kommt, wird der Nutzer eher F5 drücken.

        Mach das nicht mit zahlenden Kunden.

        Ich gebe den Benutzer später aus über Hallo $benutzer. Ich escape generell alle Inputs über das Teil, damit keiner HTML-Code einschleust.
        Dafür bearbeite ich die Rückgaben von der DB gar nicht, weil ich davon ausgehe, dass die bereits entsprechend bearbeitet sind.

        Es hat sich nicht bewährt, nicht mit Rohdaten zu arbeiten, solange es geht, und erst dann die jeweils konkreten Anpassungen für das konkrete Ziel vorzunehmen. Du siehst das ja bereits an deinem Fall, dass es zu Problemen führt.

        Ob ich nun " oder &qout; in der DB speichere ist jetzt auch egal. Das Ergebnisse ist für den Nutzer das Gleiche.

        Naja, &quot; verbraucht 5 Zeichen mehr. Damit hast du ein weiteres potentielles Problem, dass eigentlich passend lange Strings abgeschnitten werden. Und dann erinnere dich mal daran, dass der String in Wirklichkeit doch zu lang ist.

        dedlfix.

        1. Tach!

          Findest du es für Benutzer sonnvoll, bei einem Datenbankfehler die Arbeit sang- und klanglos einzustellen?
          Ja, weil sonst eh nix läuft. Ob ich nun eine Fehlermeldung ausspucke oder einfach nix kommt. Wenn nix kommt, wird der Nutzer eher F5 drücken.

          Mach das nicht mit zahlenden Kunden.

          Ich werde das anpassen und hier eine kleine rote Meldung basteln. Obwohl ich keine zahlenden Kunden habe. Wäre auch höchst bedenklich bei diesen Einsteigerfragen^^

          Es hat sich nicht bewährt, nicht mit Rohdaten zu arbeiten, solange es geht, und erst dann die jeweils konkreten Anpassungen für das konkrete Ziel vorzunehmen. Du siehst das ja bereits an deinem Fall, dass es zu Problemen führt.

          OK, werde htmlspecialchars() nur noch dort verwenden, wo die Variable direkt per echo ausgegeben wird

          Ich werde mich mal in diese prepared Statements weiter einlesen. bisher habe ich den Aufbau noch nicht so ganz verstanden. Wenn ich es schaffe die Dinger zu verwenden, ist eine Verwendung von mysqli_real_escape_string() weiterhin sinnvoll oder ist das durch die prepared Statements dann überflüssig?

          1. Tach!

            OK, werde htmlspecialchars() nur noch dort verwenden, wo die Variable direkt per echo ausgegeben wird

            Das ist mal ein Anfang. Das musst du aber nun auch konsequent immer anwenden, wenn Daten in einen anderen Kontext eingefügt werden. Dazu musst du diese Kontextwechsel erkennen und dann auch wissen, was der jeweilige Zielkontext für Besonderheiten hat und welche Werkzeuge dafür helfen. Alle im Web gängigen Kontextwechsel stehen in unserem Wiki: http://wiki.selfhtml.org/wiki/Artikel:Kontextwechsel

            Ich werde mich mal in diese prepared Statements weiter einlesen. bisher habe ich den Aufbau noch nicht so ganz verstanden.

            Einfach gesagt erstellst du dein Stetement nur noch mit Platzhaltern statt Daten. Die dazugehörigen Daten werden in einem weiteren Schritt übergeben. Dieses Binding ordnet die Daten (oder die Variablen, in denen diese Daten enthalten sind) den jeweiligen Platzhaltern zu. Um weitere Dinge musst du dich nicht kümmern, den ordnungsgemäßen Transport, so dass es nicht zu Missverständnissen zwischen Statement und Daten kommt, übernimmt die in PHP eingebundene MySQL-Client-API.

            Wenn ich es schaffe die Dinger zu verwenden, ist eine Verwendung von mysqli_real_escape_string() weiterhin sinnvoll oder ist das durch die prepared Statements dann überflüssig?

            Das ist nicht mehr notwendig, weil du keine Daten mit Code vermischst. Somit können nicht mehr bestimmte Zeichen in den Daten als Begrenzungsende interpretiert werden, was es sonst zu verhindern gab. Das sollte klarer werden, wenn du die Einleitung vom Kontextwechselartikel gelesen hast.

            dedlfix.

            1. Hallo dedlfix,

              danke für deine Antworten. Wirklich sehr hilfreich.

              In vielen Foren wird beschrieben, das man prepared Statements nur verwenden soll, wenn Benutzereingaben verarbeitet werden, wenn jedoch intern Daten verarbeitet werden, kann man wieder normale Abfragen nehmen, da diese schneller verarbeitet werden (Einzelabfrage).

              Hier sehe ich genau das Problem des Kontextwechsels, da ja vorher über prepared Statements ungeprüfte Angaben ausgelesen und dann in eine normale Abfrage eingebunden werden.
              Also doch durchweg prepared Statements verwenden?

              Ich habe gelesen, dass bei einem guten Programmdesign, bei dem Datenzugriffe und der Rest des Programm getrennt sind, es einfach ist, sicherzustellen das alle Abfrage vorher prepared sind.

              Ist hiermit gemeint, das alle Abfragen in entsprechende Funktionen ausgelagert werden?

              Donald

              1. Tach!

                In vielen Foren wird beschrieben, das man prepared Statements nur verwenden soll, wenn Benutzereingaben verarbeitet werden, wenn jedoch intern Daten verarbeitet werden, kann man wieder normale Abfragen nehmen, da diese schneller verarbeitet werden (Einzelabfrage).

                Es gibt keinen Unterschied zwischen Nutzereingaben und Daten aus anderen Quellen. Für alle muss der Kontextwechsel beachtet werden. Interne Daten müssen sich genauso an die Syntaxregeln SQLs halten.

                Hier sehe ich genau das Problem des Kontextwechsels, da ja vorher über prepared Statements ungeprüfte Angaben ausgelesen und dann in eine normale Abfrage eingebunden werden.
                Also doch durchweg prepared Statements verwenden?

                Man muss immer den Kontextwechsel beachten oder ihn durch Verwendung von Dingen wie Prepared Statements ausschließen.

                Ich habe gelesen, dass bei einem guten Programmdesign, bei dem Datenzugriffe und der Rest des Programm getrennt sind, es einfach ist, sicherzustellen das alle Abfrage vorher prepared sind.
                Ist hiermit gemeint, das alle Abfragen in entsprechende Funktionen ausgelagert werden?

                Es gibt eine ganze Menge Möglichkeiten, seinen Code zu strukturieren und Teile davon zu separieren. Funktionen sind eine davon und reichen recht gut für kleinere Projekte aus.

                dedlfix.

            2. Moin!

              Wenn ich es schaffe die Dinger zu verwenden, ist eine Verwendung von mysqli_real_escape_string() weiterhin sinnvoll oder ist das durch die prepared Statements dann überflüssig?

              Das ist nicht mehr notwendig, weil du keine Daten mit Code vermischst. Somit können nicht mehr bestimmte Zeichen in den Daten als Begrenzungsende interpretiert werden, was es sonst zu verhindern gab. Das sollte klarer werden, wenn du die Einleitung vom Kontextwechselartikel gelesen hast.

              "nicht mehr notwendig" ist schlecht formuliert. Mit dem Einsatz von *_real_escape_string in Prepared Statements macht man sich die Daten kaputt, man darf es also nicht zusammen einsetzen.

              "Nicht notwendig" klingt so nach "kann man machen, braucht man aber nicht". "Darf man nicht" wäre korrekt.

              - Sven Rautenberg