Sicheres Eintragen in MySQL

schiese

Erfahrenes Mitglied
Hallo,
ich habe nach vielen Jahren wieder mit PHP angefangen und stehe nun vor einem Problem, welches mir früher nicht so bewusst war: das sichere Eintragen von Daten in die Datenbank.
Ich habe schon auf vielen Seiten einiges dazu gelesen und jetzt mehr offene Fragen als vorher.
Zum Schutz vor JavaScript-Code wie:
Code:
<script>alert('Text');</option>
genügt es die HTML-Sonderzeichen mit htmlspecialchars umzuwandeln oder gleich mit strip_tags zu entfernen. Zum Schutz vor SQL-Injections müssen bestimmte Zeichen für die Verwendung in einer SQL-Anweisung im String mit mysqli_real_escape_String maskiert werden.
So weit ist das klar. Wenn ich jetzt den folgenden String
PHP:
$text="McDonald's"
in die Datenbank speicher
PHP:
"INSERT INTO tabelle (id, lokal) VALUES (NULL, mysqli_real_escape_string($db,$text))";
steht in der DB McDonald's. Lass ich mir den Query vorher anzeigen, steht da McDonald\'s. Speichert er nicht den maskierten String oder läuft bei mir im weiteren Verlauf etwas falsch?

Sind diese Vorsichtsmaßnahmen auch nötig, wenn ich in der Tabelle ein Feld vom Typ int habe und mit
PHP:
(int)$eingabe
oder
PHP:
intval($eingabe)
sicherstelle, dass meine Variable vom Typ int ist?
Wie schaut es aus, wenn ein Feld vom Typ varchar ist und ich vorher mit
PHP:
preg_match("/^[\p{L}0-9\-\+\,\. ]+$/u",$text)
sicherstelle, dass in meiner Zeichenkette nur Unicode-Zeichen, Ziffern, Kommatas, Punkte, Plus- und Minuszeichen habe? Ist ein Escapen des Stringes in diesem Fall auch nötig? Ich stelle jaim vornherein sicher, dass keien"gefährlichen" Zeichen eingetragen werden können.

Viele Grüße
schiese
 
Zuletzt bearbeitet:
Hi

htmlspecialchars: Ist für die DB egal, aber nicht für die Ausgabe Richtung Browser. Also die Funktion sollte auf Daten angewendet werden, die der Benutzer sieht, aber ob sie vor DB-Eintragen oder nach jeder Abfrage (vor echo) aufgerufen wird, ist egal. strip_tags passt zwar sicherheitsmäßig auch, aber ist je nach Fall evt. nicht so gut, weil es ja den Inhalt ändert (Extrembeispiel, der Benutzer soll HTML-Code eintragen, der dann in der DB gespeichert wird. strip_tags macht natürlich alles kaputt).


Das mit mysqli_real_escape_string ist vom Verständnis her richtig (unbedingt vor DB-EIntrag etc. diesmal),
allerdings verwendest du es im Code falsch:
PHP:
$sql = "INSERT INTO tabelle (id, lokal) VALUES (NULL, mysqli_real_escape_string($db,$text))";
//und dann $sql an die DB senden
Hier ist mysqli_real_escape_string ein Teil von der SQL-Anweisung. Es ist aber eine PHP-Funktion...
PHP:
$sql = "INSERT INTO tabelle (id, lokal) VALUES (NULL, " . mysqli_real_escape_string($db, $text));
Also zuerst den Text "INSERT INTO tabelle (id, lokal) VALUES (NULL, " und
dann das Ergebnis der Funktion (statt den Funktionsnamen usw.) anhängen.

Integers, die mit intval etc. geprüft wurden, müssen nicht escaped werden (schadet aber auch nicht)

Eigene Regexp-Prüfungen für Text wären prinzipiell möglich, nur sind die Regexp evt. nicht streng genug. Dein Ausdruck erlaubt "nur Unicode-Zeichen" und noch paar Sachen? Unicodezeichen sind "alles". Inklusive aller Zeichen, die für die DB problematisch sind. Also besser (zusätzlich auch) escapen, wenn man sich nciht wirklich 100% sicher ist, alle Probleme verstanden und beachtet zu haben (bzw. sogar dann. Tut ja nicht weh, und man vermeidet Probleme, wenn man sich doch geirrt hat)
 
Zuletzt bearbeitet:
Hallo sheel,
vielen Dank für deine Antwort. Wenn ich die Funktion wie von dir beschrieben anwende, werden keine Daten in die Datenbank eingetragen. Nur wenn ich noch einfache Anführungszeichen drumsetze:
PHP:
$sql = "INSERT INTO tabelle (id, lokal) VALUES (NULL, '" . mysqli_real_escape_string($db, $text)."')";
Zum eigentlichen Problem, dem Speichern der Daten in der Datenbank bin ich nicht weitergekommen. Werden die Daten nur maskiert übergeben aber nicht maskiert gespeichert? Dazu finde ich leider nichts. Speicher ich z.B. "geht's" (ohne Anführungszeichen) gibt mir mysqli_real_escape_string "geht\'s" aus, in der DB steht jedoch normal "geht's".

Viele Grüße
schiese
 
Ok, die einfachen Anführungszeichen hab ich vergessen :)

Ja, die Daten werden normal unmaskiert gespeichert.
Das ganze Escapen, also zB. ersetzen von ' durch \' usw., ist nur, damit die DB erkennen kann welche Zeichen Teil der SQL-Anweisung und welche Teil der Daten sind. Ein ' ist eben für die Anweisung, und ein \' ist ein ' in den Daten. Gespeichert (bei Inserts) wird nur das ' und so bekommt man es bei Abfragenergebnissen auch zurück
 
Ah OK. Jetzt habe ich es wohl verstanden. Jemand kann einen "schadhaften" String eingeben. Wenn ich ihn mit mysqli_real_escape_string () speicher kann er keinen Schaden anrichten wird aber unmaskiert gespeichert. Lese ich ihn nächstes mal mit SELECT aus und benutze bei einem UPDATE diesen ausgelesenen Wert (der Sinn sei dahingestellt), kann er prinzipiell wieder gefährlich sein, da er unmaskiert gespeichert wurde. Ist das so richtig?

Viele Grüße
schiese
 
Hi,

ja, im Prinzip schon. Du musst also immer folgende Wege sichern:

Benutzer -> Datenbank (sql escapen oder noch besser: Prepared Statements)
Datenbank -> Benutzer (html escapen)

Das "doppelte" escapen scheint zwar unperformanter zu sein, vorallem weil ja jeder Select wieder neu espaced werden muss. Aber ich finde es ist besser, immer die möglichst unveränderten Daten in der Datenbank zu haben.

Grüsse,
BK
 
Zurück