Karsten
SQL-Injektion
- php
Hallo.
Um Injektionen zu vermeiden, wird im allgemeinen zum Gebrauch von mysql_real_escape_string() geraten. Laut HB werden \x00, \n, \r, , ', " und \x1a maskiert. Wenn ich alle Nutzereingaben stets kontrolliere, daß nur das enthalten ist, was ich auch als zulässige Eingabe erwarte, und ferner weiß, daß diese Zeichen (\x00, \n, \r, , ', " und \x1a) nicht enthalten sind, ist doch der Gebrauch von mysql_real_escape_string() an sich überflüssig, oder? Wenn ich z. B. einen Username teste, der nur aus Buchstaben von a-z und A-Z besteht, ist ja keins der zu maskierenden Sonderzeichen enthalten. Ich sehe eigentlich einen sinnvollen Einsatz von mysql_real_escape_string() nur bei textarea, einmal abgesehen davon, wenn ' und " auch in <input type="text"> zugelassen sein sollen (kann man ja beim Testen herausfiltern, somit wäre kein Injektion darüber möglich). \n und \r sind ja nur mit einer textarea möglich.
Ich grüsse den Cosmos,
Ich sehe eigentlich einen sinnvollen Einsatz von mysql_real_escape_string()
Und wie stellst du sicher, das jemand Daten nur über dein Formular schickt und noch von ausserhaylb deines Servers?
Möge das "Self" mit euch sein
Ich grüsse den Cosmos,
Ich sehe eigentlich einen sinnvollen Einsatz von mysql_real_escape_string()
Und wie stellst du sicher, das jemand Daten nur über dein Formular schickt und noch von ausserhaylb deines Servers?
Möge das "Self" mit euch sein
»»
Wie soll das funktionieren, wenn ich Code wie diesen habe:
if (preg_match("/^\w+$/", $_POST['input']) sql_query("... = " . $_POST['input']); ??? Ist doch egal, ob von meinem Formular oder sonstwo her, die Abfrage wird durchlaufen.
Ich grüsse den Cosmos,
if (preg_match("/^\w+$/", $_POST['input']) sql_query("... = " . $_POST['input']); ???
Und das findest du einfacher, als mysql_real_escapestring()?
Ist doch egal, ob von meinem Formular oder sonstwo her, die Abfrage wird durchlaufen.
Ja, konnte man auch riechen, denn geschrieben hast du das nicht. Sind nicht viele Hellseher hier.
Möge das "Self" mit euch sein
Ich grüsse den Cosmos,
if (preg_match("/^\w+$/", $_POST['input']) sql_query("... = " . $_POST['input']); ???
Und das findest du einfacher, als mysql_real_escapestring()?
»»
Nicht unbedingt einfacher, aber da ich die Eingaben bei komplexen Datenbanken eh immer prüfen muß, stellte sich mir die Frage, ob zusätzliches Escapen überhaupt einen Nutzen hat.
Ist doch egal, ob von meinem Formular oder sonstwo her, die Abfrage wird durchlaufen.
Ja, konnte man auch riechen, denn geschrieben hast du das nicht. Sind nicht viele Hellseher hier.
Ja, das hatte ich geschrieben:
Wenn ich alle Nutzereingaben stets kontrolliere, daß nur das enthalten ist, was ich auch als zulässige Eingabe erwarte, und ferner weiß, daß diese Zeichen (\x00, \n, \r, , ', " und \x1a) nicht enthalten sind, ist doch der Gebrauch von mysql_real_escape_string() an sich überflüssig, oder?
Ich grüsse den Cosmos,
Nicht unbedingt einfacher, aber da ich die Eingaben bei komplexen Datenbanken eh immer prüfen muß, stellte sich mir die Frage, ob zusätzliches Escapen überhaupt einen Nutzen hat.
Naja, wieso haben Autos ein Türschloss und zusätzlich ein Zündschloss? Wenn man mal das Abschliessen der Tür vergisst, kann trotzdem nicht jeder wegfahren ;)
Möge das "Self" mit euch sein
Hi!
Ich sehe eigentlich einen sinnvollen Einsatz von mysql_real_escape_string() nur bei textarea, einmal abgesehen davon, wenn ' und " auch in <input type="text"> zugelassen sein sollen (kann man ja beim Testen herausfiltern, somit wäre kein Injektion darüber möglich). \n und \r sind ja nur mit einer textarea möglich.
Und wer garantiert dir, daß ein Angreifer auch dein Formular benutzt?
Um Daten an dein Script zu schicken, brauche ich dein Formular nicht.
Schöner Gruß,
rob
Um Daten an dein Script zu schicken, brauche ich dein Formular nicht.
Könnte man dieser Lücke mit einer TAN (Transaktionsnummer) beikommen? Ein hidden- Feld bekommt eine generierte Nummer, die sich der Server (Datenbank) für eine gewisse Zeit merkt, z.B. 30 Minuten.
Script prüft zuerst, ob die TAN gültig ist und löscht sie aus der DB.
Falls ungültig, keine weitere Bearbeitung. So könnte man auch das mehrfache Absenden ignorieren und vielleicht sogar einen massenhaften Angriff abschwächen, weil dem "Feind" nur wenig Zeit gewidmet wird.
Kalle
Moin!
Um Daten an dein Script zu schicken, brauche ich dein Formular nicht.
Könnte man dieser Lücke mit einer TAN (Transaktionsnummer) beikommen?
Nein, eine Transaktionsnummer verhindert nicht, dass beliebige Daten an das Skript gesendet werden. Sie verhindert nur, dass Daten verarbeitet werden, die nicht angekündigt wurden. Das aber zu tun ist die leichteste Aufgabe.
- Sven Rautenberg
Moin!
Wenn ich alle Nutzereingaben stets kontrolliere, daß nur das enthalten ist, was ich auch als zulässige Eingabe erwarte, und ferner weiß, daß diese Zeichen (\x00, \n, \r, , ', " und \x1a) nicht enthalten sind, ist doch der Gebrauch von mysql_real_escape_string() an sich überflüssig, oder?
Was ist einfacher aus deiner Sicht?
1. Im Datenbankmodul vollkommen unabhängig von den tatsächlich auftretenden Nutzerdaten immer Escaping zu betreiben und damit 100% sicher zu sein, dass keine Injection stattfinden kann.
2. Oder im Datenbankmodul zu überlegen, welches Datenformat das jeweilige Feld tatsächlich hat, und darauf zu hoffen, dass das Validierungsmodul tatsächlich benutzt, fehlerfrei programmiert und korrekte Angaben durchläßt?
Aus meiner Sicht ergibt sich nahezu zwingend nur Option 1: Die Sicherheit der Applikation erreicht im Punkt SQL-Injection nur dann 100% ohne weiteres Nachdenken, wenn ebenfalls ohne weiteres Nachdenken 100% der aus Variablen in SQL-Strings integrierten Daten in diesem Kontext direkt vor ihrer Benutzung escaped werden.
Du sparst dir eventuell 0,1% CPU-Aufwand, du gewinnst aber mindestens 100% Gehirnkapazität für die übrigen Programmieraufgaben. Und verhindert 100% der durch Option 2 möglichen Programmierfehler.
- Sven Rautenberg
Moin!
Wenn ich alle Nutzereingaben stets kontrolliere, daß nur das enthalten ist, was ich auch als zulässige Eingabe erwarte, und ferner weiß, daß diese Zeichen (\x00, \n, \r, , ', " und \x1a) nicht enthalten sind, ist doch der Gebrauch von mysql_real_escape_string() an sich überflüssig, oder?
Was ist einfacher aus deiner Sicht?
Im Datenbankmodul vollkommen unabhängig von den tatsächlich auftretenden Nutzerdaten immer Escaping zu betreiben und damit 100% sicher zu sein, dass keine Injection stattfinden kann.
Oder im Datenbankmodul zu überlegen, welches Datenformat das jeweilige Feld tatsächlich hat, und darauf zu hoffen, dass das Validierungsmodul tatsächlich benutzt, fehlerfrei programmiert und korrekte Angaben durchläßt?
Aus meiner Sicht ergibt sich nahezu zwingend nur Option 1: Die Sicherheit der Applikation erreicht im Punkt SQL-Injection nur dann 100% ohne weiteres Nachdenken, wenn ebenfalls ohne weiteres Nachdenken 100% der aus Variablen in SQL-Strings integrierten Daten in diesem Kontext direkt vor ihrer Benutzung escaped werden.
Du sparst dir eventuell 0,1% CPU-Aufwand, du gewinnst aber mindestens 100% Gehirnkapazität für die übrigen Programmieraufgaben. Und verhindert 100% der durch Option 2 möglichen Programmierfehler.
- Sven Rautenberg
Die Frage ist für mich, was sinnvoller ist. Ich denke, man sollte stets alle Eingaben überprüfen. Ich hatte das Beispiel Benutzername. Wenn ich weitere Fehlfunktionen ausschließen will (Prämisse: nur [a-zA-Z]), muß ich die Eingaben sowieso überprüfen. Warum dann noch escapen? Ich vertraue doch nicht darauf, ausschließlich zu escapen und daß die Eingabe schon ein valides Format haben wird.
Fehlerfreie Programmierung: Stimmt, es kann sinnvoll sein, Plausibilität zu prüfen und trotzdem escapen. Aber nicht bei einer so einfachen Sache wie einem Benutzername.
Hi!
Ich hatte das Beispiel Benutzername. Wenn ich weitere Fehlfunktionen ausschließen will (Prämisse: nur [a-zA-Z]), muß ich die Eingaben sowieso überprüfen. Warum dann noch escapen?
Niemand zwingt dich, die Funktion mysql_real_escape_string() aufzurufen. Tu es oder laß es. Das liegt ganz bei dir.
Der Aufruf dieser Funktion ist ja kein Zwang.
Wenn du sicher programmieren willst, dann solltest du nur sicherstellen, daß ein Angriff per SQL-Injection nicht möglich ist.
Wie du das tust, ist deine Sache.
Schöner Gruß,
rob
Hi!
Ich hatte das Beispiel Benutzername. Wenn ich weitere Fehlfunktionen ausschließen will (Prämisse: nur [a-zA-Z]), muß ich die Eingaben sowieso überprüfen. Warum dann noch escapen?
Niemand zwingt dich, die Funktion mysql_real_escape_string() aufzurufen. Tu es oder laß es. Das liegt ganz bei dir.
Würde mich auch nicht zwingen lassen :)
Der Aufruf dieser Funktion ist ja kein Zwang.
Wenn du sicher programmieren willst, dann solltest du nur sicherstellen, daß ein Angriff per SQL-Injection nicht möglich ist.
Wie du das tust, ist deine Sache.
Mir ging es ja nur darum, ob ich irgendwas bei meinem Gedankengang übersehen habe. Da ich aber alle Eingaben stets überprüfe, scheint der Einsatz von mysql_real_escape_string() wirklich nur bei Textarea sinnvoll zu sein, wenn ich auch Zeichen wie " und ' zulassen will.
Ich will der Funktion nicht die Existenzberechtigung absprechen. Klar, wenn ich nur ein einfaches Gästebuch erstellen will, wo der Name des Eintragenden keiner aufwendigen Prüfung unterzogen wird, würde ich escapen.
Gruß
Karsten
Moin!
Du sparst dir eventuell 0,1% CPU-Aufwand, du gewinnst aber mindestens 100% Gehirnkapazität für die übrigen Programmieraufgaben. Und verhindert 100% der durch Option 2 möglichen Programmierfehler.
Die Frage ist für mich, was sinnvoller ist. Ich denke, man sollte stets alle Eingaben überprüfen.
Das Überprüfen des Inhalts von Benutzerdaten auf Einhaltung einer gewissen Form ist nicht falsch, hat nur nichts mit kontextspezifischem Escaping zu tun.
Benutzerdaten in PHP-Variablen fallen für mich in die Kategorie "reiner Text". Egal ob das durch noch strengere Prüfungen der äußeren Form verschärft wurde. Wenn ich "reinen Text" in die Datenbank schreiben, muß ich immer damit rechnen, dass SQL-störende Sonderzeichen darin enthalten sind (warum auch immer - absichtlich oder durch Programmierfehler), und folglich ist Escaping zwingend angezeigt.
Dasselbe Spielchen gibts bei der HTML-Ausgabe. Die Inhalte der PHP-Variablen sind "reiner Text", können also HTML-störende Sonderzeichen enthalten. Folglich ist Escaping zwingend angezeigt, jetzt eben mit htmlspecialchars().
Dabei ist mir am Ort der Variablenausgabe (SQL, HTML, Dateinamen etc.) vollkommen egal, ob $username nur aus /[1]+$/ besteht. Ich vertraue einfach nicht den zu einer ganz anderen Zeit von evtl. einem ganz anderen Programmierer geschriebenen Subsystemen.
Zumal dieses grundsätzliche Escaping nicht nur eine zusätzliche Sicherheitsschicht ergibt, sondern auch die Prüfung des Usernamens vereinfacht: Ich muß nur noch meine Formprüfung beim Anlegen eines neuen Users vornehmen, und nur der geprüfte Username kann in die Datenbank gelangen. Alle weiteren lesenden Aktionen mit dem Usernamen hingegen kann ich auch formungeprüft in der Datenbank vornehmen - existierende User werden dort nur gefunden, wenn der aktuell angegebene Username der Form entspricht. Durch das Escaping (das sehr unaufwendig ist) spare ich mir das ständige Prüfen des Usernamens auf Einhaltung des Regex (was deutlich aufwendiger ist).
Natürlich hast du im Grundsatz deiner Überlegung recht: Wenn die einzigen Zeichen, die in $username vorkommen dürfen, immer unverändert durch das Escaping durchgehen, könnte man sich das Escaping auch sparen.
Aber ich halte eben absolut nichts davon, Sicherheitsmechanismen nur aufgrund von zufälligen, abänderbaren Überlegungen und Definitionen z.B. über den Inhalt eines Benutzernamens, zu umgehen. Wenn ich den Datenbanklayer programmiere, gehe ich reflexartig davon aus, dass alle mir übergebenen Daten böse Zeichen enthalten, und handle entsprechend.
Ich hatte das Beispiel Benutzername. Wenn ich weitere Fehlfunktionen ausschließen will (Prämisse: nur [a-zA-Z]), muß ich die Eingaben sowieso überprüfen. Warum dann noch escapen? Ich vertraue doch nicht darauf, ausschließlich zu escapen und daß die Eingabe schon ein valides Format haben wird.
Escaping ist keine Prüfung der Form. Aber die Prüfung der Form muß ja nicht überall stattfinden, wo du einen Benutzernamen übergeben bekommst - es reicht ja aus, dort zu prüfen, wo der Benutzername neu erzeugt wird (bzw. verändert werden kann).
Fehlerfreie Programmierung: Stimmt, es kann sinnvoll sein, Plausibilität zu prüfen und trotzdem escapen. Aber nicht bei einer so einfachen Sache wie einem Benutzername.
Um Escapingprobleme zu bekommen, reicht es doch schon aus, den RegEx fehlerhaft zu programmieren. Statt /[2]+$/ schreibst du vielleicht nur /[3]+/ - und schwups kann man dir SQL-aktive Zeichen unterschieben, die mit nachgeschaltetem Escaping absolut keine Wirkung haben - und ohne Escaping böse Sicherheitslücken reißen.
An wievielen Stellen taucht in deinem Code die Notwendigkeit zur Prüfung des Usernamens auf Einhaltung der Form auf? Hoffentlich nur an einer einzigen Stelle, aber vermutlich an mehreren. Die müssen alle synchron gehalten werden. Und spätestens dann, wenn der Username mehr Zeichen enthalten darf, weil das so gefordert ist, ist es doch ein deutlich beruhigenderes Gefühl, wenn die Datenbank nicht abkacken kann, weil der DB-Code noch escaping veranstaltet.
Es ist halt ein Erfahrungswert aus tausenden von programmierten Skripten: Die Datenbanksicherheit wird sehr häufig durch mangelndes Escaping gefährdet. Wenn man sich als Programmierer daher angewöhnt, variable Daten immer zu escapen, egal was drinsteht, wird man in diese Sicherheitsfalle nie mehr hineintappen. Das Gleiche gilt für die HTML-Ausgabe: Wenn man sich angewöhnt, variable Daten immer durch htmlspecialchars() zu entschärfen, wird man niemals unerwartet fremden HTML- oder Javascriptcode auf seiner Seite haben.
- Sven Rautenberg
Moin!
Hallo.
Das klingt schon vernünftig, was Du schreibst. Es war auch mehr eine theoretische Frage. Von der CPU-Beanspruchung her gesehen ist das Escapen natürlich kein Ding. Aber es ist eben mehr Schreibaufwand (schwaches Argument!?). Weiterhin halte ich manche Dinge, wie sie in Standardtutorials genannt werden, für schwachsinnig. Du erwähntest htmlspecialchars, was Du routinemäßig bei der HTML-Ausgabe anwendest. Auch da könnte man natürlich wieder argumentieren, bei einzig erlaubten Zeichen [a-z] ist das nicht nötig. Darauf will ich aber gar nicht hinaus, das mache ich im Grunde nämlich genauso (mit derselben Überlegung, die Du zum Escapen von SQL-Eingaben vorgetragen hast, daß es zuviel Aufwand macht, sich jedes mal den Kopf zu zerbrechen, ob eine Maskierung mit htmlspecialchars notwendig ist oder nicht). Was ich sagen will, ist, in Tutorials werden oft auch völlig sinnfreie Behauptungen aufgestellt, die sich zu Mythen verselbständigen, weil die Leute nicht selbst darüber nachdenken und das eine Tutorial vom anderen kopiert. Z. B. wird oft strip_tags empfohlen. Das ist völlig unnötig, wenn Du Ausgaben mit htmlspecialchars maskierst, und obendrein kontraproduktiv, weil der Befehl unzuverlässig arbeitet und schon mal mehr entfernt als gewollt. Die Praxis hat mich gelehrt, auf diesen Befehl zu verzichten. Und deshalb habe ich mir überlegt, ob es wirklich Sinn macht, den mannigfaltigen Tutorials Glauben zu schenken, Eingaben stets vorm Eintragen in eine Datenbank zu escapen.
Moin!
Was ich sagen will, ist, in Tutorials werden oft auch völlig sinnfreie Behauptungen aufgestellt, die sich zu Mythen verselbständigen, weil die Leute nicht selbst darüber nachdenken und das eine Tutorial vom anderen kopiert. [...] Und deshalb habe ich mir überlegt, ob es wirklich Sinn macht, den mannigfaltigen Tutorials Glauben zu schenken, Eingaben stets vorm Eintragen in eine Datenbank zu escapen.
Was das Escaping angeht: Ja, das ist in jedem Fall sinnvoll, wenn es immer angewandt wird.
Und was strip_tags() angeht, bin ich genauso deiner Meinung, dass es die falsche Methode ist, sich gegen HTML-Injection abzusichern.
- Sven Rautenberg
Was das Escaping angeht: Ja, das ist in jedem Fall sinnvoll, wenn es immer angewandt wird.
Ok, ich habe mich überzeugen lassen. Dann hätte ich noch eine Frage: In Tutorials werden immer nur die Benutzereingaben ecapet (oder escaped ??). Ich denke, das Escaping kann man nicht auf die gesamte Query anwenden!? Statt
$query = "UPDATE datenbank SET name='" . mysql_real_escape_string($_POST['benutzername']) . "'";
$query = mysql_real_escape_string("UPDATE datenbank SET name='" . $_POST['benutzername'] . "'");
Dann könnte man das Escaping einmal zentral in einer Funktion/ Methode notieren und die Funktion immer unescapet aufrufen
$query = "UPDATE datenbank SET name='" . $_POST['benutzername'] . "'";
db_query($query);
Ich glaube, mysql_real_escape_string() ersetzt nur stumpf die SQL-Sonderzeichen, ohne die Logik einer SQL-Anweisung parsen zu können. Im Beispiel würde wohl auch das ' von SET name=' maskiert, sehe ich das richtig? Gibt es irgendeinen Programmiertrick, wie man dennoch erreichen kann, sich das Maskieren jedes einzelnen Benutzereintrags zu ersparen und nur einmal zentral zu escapen? Ich hätte zwar noch eine Idee (die Query von einer Funktion zusammenbauen lassen und dieser die einzelnen Bestandteile der Query als Array übermitteln), aber die erscheint mir zunächst einmal zu aufwendig.
Hi,
Ok, ich habe mich überzeugen lassen. Dann hätte ich noch eine Frage: In Tutorials werden immer nur die Benutzereingaben ecapet (oder escaped ??). Ich denke, das Escaping kann man nicht auf die gesamte Query anwenden!? Statt
$query = "UPDATE datenbank SET name='" . mysql_real_escape_string($_POST['benutzername']) . "'";
$query = mysql_real_escape_string("UPDATE datenbank SET name='" . $_POST['benutzername'] . "'");
Nein, das geht natürlich nicht.
Beispiel: das ' nach name= muß ja unverändert erhalten bleiben, würde aber mit \ escaped. Ein ' in einem mit '' eingegrenzten Wert dagegen muß escaped werden.
cu,
Andreas
Ahoi Karsten,
...(Prämisse: nur [a-zA-Z])...
und was ist mit den Usern 'Peter73' und 'Karl-Heinz'?!?
MfG