htmlentities und mysql_real_escape_string
Andy89
- php
Einen schönen guten Tag,
gestern wurde mir erklärt, dass ich gegen MySql Injectionen meine POST/GET werte schützen soll. Wenn ich ein Insert Into durchführe würde nun so vorgehen:
$var1 = mysql_real_escape_string($_POST['var1']);
$var2 = mysql_real_escape_string($_POST['var2']);
1.Meine Fragen, muss ich das auch bei Radio und Checkbox durchführen?
2.muss ich bei Textfeldern bzw Textarea
$var2 = mysql_real_escape_string($_POST['var2']);
$var2 = htmlentities($var2);
so vorgehen?
viele Grüße und Danke für die Hilfe
Andy
Mahlzeit,
1.Meine Fragen, muss ich das auch bei Radio und Checkbox durchführen?
Merke: ALL INPUT IS EVIL! Du kannst nicht sicher sein, dass das, was Dein Skript an "Formular"-Daten empfängt, tatsächlich von Deinem Formular abgesendet wurde. Theoretisch kann ich mit einem eigenen Formular, dessen Ziel Dein Skript ist, beliebige Daten an Dein Skript senden - und da kann natürlich in Parametern, die in Deinem Formular Radio- oder Checkboxen waren, auch etwas anderes drinstehen.
2.muss ich bei Textfeldern bzw Textarea
$var2 = mysql_real_escape_string($_POST['var2']);
Ja, wenn Du die Daten in eine Datenbank einfügen willst.
$var2 = htmlentities($var2);
Ja, wenn Du die Daten als HTML ausgeben willst. Behandle Daten IMMER kontextabhängig.
MfG,
EKKi
Hallo Ekki und Sven,
vielen Dank für die ausführlichen Antworten. Die waren Anfängerverständlich.
» Theoretisch kann ich mit einem eigenen Formular, dessen Ziel Dein Skript ist,
» beliebige Daten an Dein Skript senden - und da kann natürlich in Parametern, die » in Deinem Formular Radio- oder Checkboxen waren, auch etwas anderes drinstehen.
das wusste ich absolut nicht.
Nun eine Antwort auf meine zweite Frage war ja und eine nein:)
Ich denke ich habe verstanden wo der unterschied ist.
Wenn ich mich für das Ja (Ekki) entscheide, dann habe ich keine "Mehrarbeit" beim auslesen kann aber eventuell Probleme beim Durchsuchen der DB bekommen ?
Wenn ich mich für das Nein (Sven) entscheide steht der Text so wie er ist in der Tabelle und ich müsste diesen dann ungefähr so ausgeben :
echo htmlspecialchars($_row['feld']); ?
Ich denke ich werde mir diesen Schritt angewöhnen
"INSERT INTO tabelle SET wert1 = '".mysql_real_escape_string($_POST['var1'])."'";
und alles andere vergessen.
Ich hoffe ich habe euch richtig verstanden.
Andy
Mahlzeit,
» Theoretisch kann ich mit einem eigenen Formular, dessen Ziel Dein Skript ist,
» beliebige Daten an Dein Skript senden - und da kann natürlich in Parametern, die » in Deinem Formular Radio- oder Checkboxen waren, auch etwas anderes drinstehen.das wusste ich absolut nicht.
Dein Formular und das Skript, was die Daten verarbeitet, müssen ja in keinster Weise miteinander verbunden sein. Beispiel (aufs Wesentliche gekürzt):
Angenommen, Dein Formular sähe so aus:
foo.html:
----------
<form action="bar.php">
<input type="radio" name="fernsehen" value="ja" />
<input type="radio" name="fernsehen" value="nein" />
<input type="radio" name="fernsehen" value="vielleicht" />
</form>
Weiter angenommen, Dein Skript wäre unsicher und unsauber programmiert:
bar.php:
----------
[...]
mysql_query("INSERT INTO medienkonsum SET fernsehen = '".$_GET["fernsehen"]."'");
[...]
Wenn ich weiß, wo ich Dein Skript "bar.php" finden kann - was sollte mich also davon abhalten, einfach mal folgendes Formular zu basteln und zu schauen, was passiert, wenn ich dort beliebige Daten (z.B. den String "test'; DELETE FROM medienkonsum; SELECT * FROM medienkonsum WHERE '' = '") eingebe?
"Bösewicht":
evil.html:
----------
<form action="http://www.example.org/bar.php">
<input type="text" name="fernsehen" />
</form>
Nun eine Antwort auf meine zweite Frage war ja und eine nein:)
Ich denke ich habe verstanden wo der unterschied ist.
Wenn ich mich für das Ja (Ekki) entscheide, dann habe ich keine "Mehrarbeit" beim auslesen kann aber eventuell Probleme beim Durchsuchen der DB bekommen ?
Ich meinte, dass Du htmlentities() erst anwenden solltest, wenn Du konkret eine Ausgabe in/als HTML machst ... diese Funktion VOR dem Schreiben in eine Datenbank auf einen String anzuwenden, ist - wie Sven ja bereits schrieb - zutiefst widersinnig und schadet eigentlich nur.
Wenn ich mich für das Nein (Sven) entscheide steht der Text so wie er ist in der Tabelle und ich müsste diesen dann ungefähr so ausgeben :
echo htmlspecialchars($_row['feld']); ?
Genau. Das, was Du aus der Datenbank herausholst, musst Du vor der Ausgabe in ein HTML-Dokument entsprechend behandeln.
Ich denke ich werde mir diesen Schritt angewöhnen
"INSERT INTO tabelle SET wert1 = '".mysql_real_escape_string($_POST['var1'])."'";
und alles andere vergessen.
Guter Plan! :-)
MfG,
EKKi
echo $begrüßung;
Ich denke ich werde mir diesen Schritt angewöhnen
"INSERT INTO tabelle SET wert1 = '".mysql_real_escape_string($_POST['var1'])."'";
und alles andere vergessen.
Für den Anfang nicht schlecht. Ich finde es nicht sehr übersichtlich, Strings aus mehreren Einzelteilen per Stringverknüpfung zusammenzubauen. Dabei entstehen sehr viele '".-Zeichenkombinationen. Die Funktion sprintf() kann hier für mehr Übersicht sorgen: (Ich hab mal noch einen Wert hinzugenommen.)
sprintf("INSERT INTO tabelle SET wert1 = '%s', wert2 = '%s'",
mysql_real_escape_string($_POST['var1']),
mysql_real_escape_string($_POST['var2']));
Und dann gibt es noch eine Technik namens Prepared Statements. Diese trennt die Anweisungsteile eines SQL-Statements von den Datenbestandteilen. Das Statement und die Daten gehen getrennte Wege zur Datenbank. Ursprünglich wurden PS erfunden, um das Parsen des Statements nur einmal durchführen zu müssen, und es dann mehrmals mit Daten ausführen zu können. Das kommt im Webumfeld weniger zur Anwendung, weil hier für jeden Request eine neue Verbindung zum DBMS benutzt wird, und das alte preparierte Statement nicht mehr vorhanden ist. Doch die Trennung zwischen Statement und Daten kann man sich ganz gut zu Nutze machen. Man formuliert das Statement mit Platzhaltern, ähnlich wie bei sprintf():
"INSERT INTO tabelle SET wert1 = ?, wert2 = ?"
Beachte, dass hier die Platzhalter ohne Stringbegrenzer notiert sind. Diesen String übergibt man der Funktion, die das Statement vorbereitet. Anschließend bindet man Daten an das preparierte Statement und führt es aus. Die Daten werden dabei nicht weiter behandelt. Sie werden direkt an Funktionen der Datenbank-API übergeben, die sich um den weiteren ordnungsgemäßen Transport kümmern.
Prepared Statements kann man allerdings nicht mit der herkömmlichen mysql-Extension verwenden. Dafür benötigt man die mysqli-Extension oder PDO. Verwendungsbeispiele bitte den verlinkten Handbuchkapiteln entnehmen.
Möglicherweise wird dich das als Anfänger überfordern, aber ich wollt's nur mal erwähnt haben, dass es noch weitere Möglichkeiten gibt.
echo "$verabschiedung $name";
Hallo,
Für den Anfang nicht schlecht. ....Dabei entstehen sehr viele '".-Zeichenkombinationen. Die Funktion sprintf() kann hier für mehr Übersicht sorgen: (Ich hab mal noch einen Wert hinzugenommen.)
sprintf("INSERT INTO tabelle SET wert1 = '%s', wert2 = '%s'",
mysql_real_escape_string($_POST['var1']),
mysql_real_escape_string($_POST['var2']));
super das wäre nämlich meine nächste Frage gewesen.
(eigentlich müsste das doch ein Update sein, weil da Set steht ?)
Jedenfalls habe ich es nicht zum laufen bekommen
Ich habe in diesem Forum dazu diese Variante gefunden, getestet allerdings nicht als Funktion und geht gut, ich das Formular wird ca 250 Eingabesätze (Datensätze) haben.
$sql = sprintf("INSERT INTO tabelle
(id, var1)
VALUES ('', '%s', '%s')",
mysql_real_escape_string($_POST['var1'])
mysql_real_escape_string($_POST['var2'])
);
$result = mysql_query($sql);
Diese Variante gefällt mir gut und ist verständlich.
Möglicherweise wird dich das als Anfänger überfordern, aber ich wollt's nur mal erwähnt haben, dass es noch weitere Möglichkeiten gibt.
Mit Sicherheit
Letzlich habe ich noch zwei Fragen nur zum Verständnis
1. Ein Fragebogen mit 25 Fragekomplexen, jeder Komplex hat ca 10 Fragen mit wobei alles input type="radio" ist und es kann 1 bis 5 bewertet werden.
Kann man das in einer Datei erledigen oder sollte man die Daten Teilen und auf mehrere Seiten verteilen?
Dann müsste ich entweder die Ergebnisse von Seite zu Seite übergeben oder eine ID am Anfang vergeben, die mit übernehmen und ab der zweiten Seite ein Update durchführen?
2. mysql_real_escape_string()muss man in einer for schleife auf etwas achten? Weil ich bekam diesen Fehler:
Warning: mysql_real_escape_string() expects parameter 1 to be string, array given in
zu foreach habe ich folgendes gefunden
http://de3.php.net/manual/de/control-structures.foreach.php
Viele Grüße
Andy
echo $begrüßung;
sprintf("INSERT INTO tabelle SET wert1 = '%s', wert2 = '%s'",
mysql_real_escape_string($_POST['var1']),
mysql_real_escape_string($_POST['var2']));
(eigentlich müsste das doch ein Update sein, weil da Set steht ?)
Nein, INSERT gibt es in zwei Formen[*]. Die mit SET ist eine davon.
Jedenfalls habe ich es nicht zum laufen bekommen
Mit einer genauen Fehlerbeschreibung könnte man vielleicht sagen, woran es liegt.
$sql = sprintf("INSERT INTO tabelle
(id, var1)
VALUES ('', '%s', '%s')",
mysql_real_escape_string($_POST['var1'])
mysql_real_escape_string($_POST['var2'])
);
Abgesehen vom Nichtaufführen von var2 in der Werteliste ... Es ist nicht unbedingt erforderlich jedes Feld der Tabelle im INSERT zu berücksichtigen. Die Felder haben Default-Werte, die bei Nichtvorhandensein im INSERT-Statement in den neuen Datensatz eingefügt werden. Insbesondere wird ID so ein Feld sein, dass automatisch ausgefüllt werden soll. Lass es einfach in der Feld- und Werteliste unaufgeführt.
- Ein Fragebogen mit 25 Fragekomplexen, jeder Komplex hat ca 10 Fragen mit wobei alles input type="radio" ist und es kann 1 bis 5 bewertet werden.
Kann man das in einer Datei erledigen oder sollte man die Daten Teilen und auf mehrere Seiten verteilen?
Dem Computer ist das egal, den Probanden sicher nicht. Das musst du mit Rücksicht auf diese selbst entscheiden.
Dann müsste ich entweder die Ergebnisse von Seite zu Seite übergeben oder eine ID am Anfang vergeben, die mit übernehmen und ab der zweiten Seite ein Update durchführen?
Sessions lautet eine Antwort auf so eine Frage. Leg die Werte der einzelnen Frageseiten im Server in einer Session ab und bilde daraus beim Auswerten der letzten Frageseite dein INSERT-Statement.
- mysql_real_escape_string()muss man in einer for schleife auf etwas achten? Weil ich bekam diesen Fehler:
Warning: mysql_real_escape_string() expects parameter 1 to be string, array given in
Diese Funktion kann nur mit skalaren Werten (Strings, Integer) umgehen. Ob du eine Schleife hast oder nicht - du darfst sie nicht mit komplexen Werten (Array, Objekt) füttern. Kontrolausgaben mit mit var_dump() sind ein gutes Hilfsmittel, sich über Typ und Inhalt einer Variable oder eines Ausdrucksergebisses zu informieren. (Ein <pre> vor der Ausgabe von komplexen Strukturen erhöht die Übersichtlichkeit.)
[*] INSERT ... SELECT ist eine dritte Form, die aber für andere Anwendungsfälle gebraucht wird.
echo "$verabschiedung $name";
Hallo,
ich möchte mich bedanken, Ihr habe mir sehr geholfen.
Nein, INSERT gibt es in zwei Formen[*]. Die mit SET ist eine davon.
Mit einer genauen Fehlerbeschreibung könnte man vielleicht sagen, woran es liegt.
War meine Schuld
$sql =sprintf("INSERT INTO daten SET var1 = '%s', var2 = '%s'",
mysql_real_escape_string($_POST['var1']),
mysql_real_escape_string($_POST['var2']));
$result = mysql_query($sql);
Ich denke das ist die Form die ich mir ab heute aneignen werde, weil ich habe hier immer , Wert zu '%s' , da kann ich nicht so schnell Fehler machen.
Sessions lautet eine Antwort auf so eine Frage. Leg die Werte der einzelnen Frageseiten im Server in einer Session ab und bilde daraus beim Auswerten der letzten Frageseite dein INSERT-Statement.
Wenn Du wüsstest wie es in meinem Kopf raucht... Mit Sessions werde ich mich in absehbarer Zukunft beschäftigen.
Diese Funktion kann nur mit skalaren Werten (Strings, Integer) umgehen. Ob du eine Schleife hast oder nicht - du darfst sie nicht mit komplexen Werten (Array, Objekt) füttern. Kontrolausgaben mit mit var_dump() sind ein gutes Hilfsmittel, sich über Typ und Inhalt einer Variable oder eines Ausdrucksergebisses zu informieren. (Ein <pre> vor der Ausgabe von komplexen Strukturen erhöht die Übersichtlichkeit.)
Danke
Grüße aus Brandenburg
Andy
echo $begrüßung;
Sessions lautet eine Antwort auf so eine Frage. Leg die Werte der einzelnen Frageseiten im Server in einer Session ab und bilde daraus beim Auswerten der letzten Frageseite dein INSERT-Statement.
Wenn Du wüsstest wie es in meinem Kopf raucht... Mit Sessions werde ich mich in absehbarer Zukunft beschäftigen.
Und wenn du dir das PHP-Handbuchkapitel zu Sessions anschaust wird es sicher nicht besser werden. Dabei ist das grundlegende Sessions-Handling ein ganz einfacher Vorgang. Jedes Script, das auf Session-Variablen zugreifen möchte, muss session_start() ausführen. Dies muss passieren, bevor auch nur die kleinste Ausgabe erfolgt ist (z.B. HTML-Bereiche, Ausgaben aus PHP mittels echo, Whitespace-Zeichen, die sich nach einem ?> in Include-Dateien einschmuggeln). Man platziert session_start() deshalb gern gleich an den Dateianfang (natürlich nach dem <?php). Nun greift man auf Werte im Array $_SESSION lesend und schreibend zu wie auf jedes andere Array auch. Das wars im Prinzip schon, den Rest mach PHP im Normalfall von allein. Alle anderen Funktionen (wie session_register() oder session_destroy()) sind teilweise veraltet und nur noch aus historischen Gründen aufgeführt oder werden für den Anfang nicht benötigt.
echo "$verabschiedung $name";
Moin!
gestern wurde mir erklärt, dass ich gegen MySql Injectionen meine POST/GET werte schützen soll. Wenn ich ein Insert Into durchführe würde nun so vorgehen:
$var1 = mysql_real_escape_string($_POST['var1']);
$var2 = mysql_real_escape_string($_POST['var2']);
Ich persönlich halte diese Umkopiererei für ungünstig, denn die Variablen $va1 und $var2 müssen ja auch noch irgendwann in den Query hineinkommen - und dort taucht dann logischerweise die Funktion mysql_real_escape_string() nicht mehr auf, man kann also DORT gar nicht sehen, ob die Variablen escaped werden, oder nicht.
Deshalb: Sorge immer für Klarheit über diese Dinge:
$sqlquery = "INSERT INTO tabelle SET wert1 = '".mysql_real_escape_string($_POST['var1'])."'";
Diese Zeile enthält mehrere wichtige Aussagen sofort sichtbar:
1. Der Wert, der in die Datenbank reingeht, wird escaped.
2. Der Wert, der reingeht, kommt aus einem POST-Formular (also externe Datenquelle).
Beide Faktoren sind enorm wichtig, um das Einschmuggeln von bösartigem Code zu unterbinden. Punkt 1 ist die Beruhigung für Punkt 2. Wenn du hingegen Variablen umkopierst, aus $_POST['var1'] eine deutlich harmloser wirkende $var1 machst, und in einer weiteren Zeile dann das Escaping einfügst, kommt am Ende zum Zusammensetzen des Querys sowas raus:
$sqlquery = "INSERT INTO tabelle SET wert1 = '$var1'";
Und das sieht auf den ersten Blick - wenn man geschult ist, auf sowas zu achten - EXTREM BÖSE aus, weil nirgendwo auch nur die geringste Sicherung gegen Code-Injection steht.
Auch wenn in den Zeilen davor vielleicht Escaping etc. durchgeführt werden könnte - kannst du 100% sicher sein, dass das unter allen Bedingungen passiert? Vielleicht klemmst du das Escaping ab, weil es ungünstig in einem IF steht, oder aus Versehen auskommentiert wird, etc...
1.Meine Fragen, muss ich das auch bei Radio und Checkbox durchführen?
Du mußt Escaping IMMER durchführen, wenn du Daten aus einem String in die Datenbank schreiben willst - egal wo dieser String herkommt. Auch die von deiner Applikation selbst erzeugten Strings mußt du escapen, wenn du jede Code-Injection in die SQL-Querys ausschließen willst.
Bei MySQL ist es dankenswerterweise so, dass keine Unterscheidung zwischen Strings und Zahlen gemacht wird: Alle Daten können immer in einfache Anführungszeichen eingeschlossen werden, und innerhalb dieser Anführungszeichen ist dann mysql_real_escape_string() anzuwenden - ohne Ausnahme!
2.muss ich bei Textfeldern bzw Textarea
$var2 = mysql_real_escape_string($_POST['var2']);
$var2 = htmlentities($var2);so vorgehen?
Nein. Du erhälst vom Formular nackten Text, sozusagen "text/plain" als Mimetyp. Das Escaping für die Datenbank mit mysql_real_escape_string() macht aus diesem Texttyp einen sql-kompatiblen Typ (nennen wir ihn mal "text/x-mysqltext").
Wenn du die Datenbank wieder abfragst, erhälst du als Ergebnis wieder "text/plain". Wenn du diesen dann als HTML-Bestandteil ausgeben willst (als "text/html"), benötigst du für die Ausgabe in HTML die Funktion htmlspecialchars() - die sorgt dafür, dass alle Zeichen in "text/plain" so konvertiert werden, dass sie auch als HTML genau identisch angezeigt werden.
Es ist blödsinnig, diesen Schritt schon vor der Speicherung in der Datenbank zu erledigen, denn es behindert dich beim Suchen in der Datenbank und stört vor allem dann, wenn du außer der Ausgabe in HTML noch weitere Zielausgabeformate hast oder irgendwann dazu bekommen könntest. Denn dann mußt du die HTML-Codierung rückgängig machen, um für das andere Zielformat neu zu codieren oder zu escapen.
- Sven Rautenberg