3.3k Aufrufe
Gefragt in Skripte(PHP,ASP,Perl...) von
Hallo,

ich bin mal wieder an einen Punkt angelangt, wo ich auf eure Tipps hoffe. Thema steht ja im Betreff. Ausgangssituation ist die folgende:

In meinem Tool gibt es mittlerweile an die 30 Seiten mit jeweils unterschiedlichen Eingabemasken. Dass die eingegebenen Daten geprüft werden müssen, weiß ich. Mir stellt sich grade die Frage, wie ich das am geschicktesten anstellen kann.

Grundsätzlich habe ich magic_quotes deaktiviert und habe mich selbst um das escapen mit real_escape_string gekümmert. Ich habe es dann immer in den SQL-String vor jede Variable gepackt, was allerdings ziemlich viel SChreibaufwand ist. Macht es mehr Sinn, nach einem Submit einfach in einer Schleife die Daten zu bereinigen? Wie macht ihr das? Und wenn ich eine Eingabe vorher z.b. auf Zahlen prüfen, etwa mit ctype_digit, ist es dann überhaupt noch nötig, so etwas wie stripslashes anzuwenden, wenn ich schon weiß, dass die Daten korrekt sind?
Wie ist das mit striptags? Nutzt ihr das für jede Eingabe? Ich habe teilweise auch Eingabebereiche, wo etwa HTML eingegeben werden muss...Wenn ich nun z. B. in einer Config-Datei über jede Eingabe striptags drüberlaufen lassen, ist der HTML-Code ja wieder weg. Um das zu umgehen, müsste ich nun aber wieder für jede Variable einzeln die Funktion ausführen..irgendwie ist das doch ein Teufelskreis??

Hoffe ihr könnt mir ein paar Tipps geben, wie ihr sowas löst.

Viele Grüße

10 Antworten

0 Punkte
Beantwortet von son_quatsch Experte (5.3k Punkte)
Ah - zum Glück wiedergefunden :-) Hier hab ich einen Generalisierungsansatz: das Prinzip besteht darin, alle möglichen aufzutretenden Parameter auf nur die zu filtern, die man auch erwartet. Und je nach Definition werden die auch entsprechend sichergestellt. In dem Beispiel ist auch noch mit drin, wie die Werte dann später in ein SQL-Kommando eingefügt werden sollen.

Ich habe es dann immer in den SQL-String vor jede Variable gepackt, was allerdings ziemlich viel SChreibaufwand ist.
Das kommt drauf an, ob du vorher die Daten noch anders auswerten musst. Wenn du real_escape_string() wie in meinem Beispiel bereits auf alle als z.B. "string" definierten Parameter anwendest und vor dem SQL-Kommando nochmal drauf zugreifst, dann sind die Daten bereits "verfälscht" (also z.B. ein Hochkomma durch ein Backslash + Hochkomma ersetzt -> "Hans' Nachbar" wird zu "Hans\' Nachbar").

Eine Vereinfachung wäre allerdings, sich eine Funktion zu basteln, der man unbegrenzt viele Variablen übergibt. Die Funktion selbst führt dann auf jede Variable einen escape-Mechanismus aus (wie auch immer) - und damit sind die Variablenwerte entsprechend verändert (=abgesichert). Sinnvoll natürlich, wenn solch ein Funktionsaufruf unmittelbar vor dem erstellen des SQL-Kommandos erfolgt - also möglichst spät.

Macht es mehr Sinn, nach einem Submit einfach in einer Schleife die Daten zu bereinigen?
Jein. Es geht halt nicht auf ganz "einfachem" Wege - damit schafft man sich immer auch Nachteile. Grundsätzlich muss man sich über sein Datenmodell genau im klaren sein (heißt: welcher Parameter ist für was gedacht, welche Werte kann er annehmen und welche darf er bloß annehmen). Mach dir bewusst, welche Parameter alle existieren. Je nach deren Typ kannst du dann für alle mein Beispiel verwenden.

Und wenn ich eine Eingabe vorher z.b. auf Zahlen prüfen, etwa mit ctype_digit, ist es dann überhaupt noch nötig, so etwas wie stripslashes anzuwenden, wenn ich schon weiß, dass die Daten korrekt sind?
Nein. Und wenn, dann ist stripslashes() nur für Werte gedacht, die du aus der Datenbank ausliest (statt sie dort hineinzuschreiben).

Nutzt ihr das für jede Eingabe?
Überhaupt nicht - dir muss nur klar sein, ob und wie du später solche Daten ausgibst. Wenn Nutzer bewusst HTML eingeben dürfen, dann darfst du da natürlich nichts strippen. Wenn sie es nicht dürfen, dann gib später einfach deren Text durch htmlentities() aus - dann wird das HTML selbst escaped.


Ich les aus all deinen Fragen heraus, dass du HTML, PHP und SQL wohl noch nicht eindeutig voneinander abgrenzen kannst. Vielleicht hast du noch ganz andere Fragen..?
0 Punkte
Beantwortet von supermax Experte (4.8k Punkte)
Ich habe mir für meine Projekte angewöhnt, escaping erst dort zu betreiben, wo es erforderlich ist, d.h. htmlspecialchars()/htmlentities() nur bei der Ausgabe, mysql_real_escape_string() direkt vor der entsprechenden SQL-Query.

Bzw. habe ich mir einen Satz von Funktionen / Methoden für häufig vorkommende Datenbankoperationen wie INSERT/REPLACE und UPDATE erstellt, die mir das escapen der Werte abnimmt (und auch NULL-Werte, Booleans und Arrays behandelt), sodaß ich einfach nur die zu ändernden/einzufügenden Daten in ein assoziatives Array packen muß und dieses einfach als Parameter an die Funktion bzw. Methode übergebe.
0 Punkte
Beantwortet von
Erstmal danke für die guten Ideen.

Zuerst möchte ich kurz erwähnen, dass ich schon HTML von PHP und SQL abgrenzen kann. Die Geschichte mit dem stripslashes, dsa ist jetzt mein Fehler, hatte ich aus einer Funktion geklaut aus einem Projekt, wo diese über jede mögliche Eingabe drübergelaufen ist. Sie sah so aus:


function cleanText($string) {
$string = trim($string);
$string = strip_tags($string);
$string = htmlspecialchars($string, ENT_QUOTES); # also convert '
$string = preg_replace('/\r\n|\r|\n/', '<br />', $string); # convert \r and \n

return $string;
}



Und deshalb bin ich ins grübeln gekommen - weil ich nicht so "streng" mit meinem Daten umgegangen bin - ob mein Code zu unsicher ist.

Grundsatzfrage (abgesehen davon, dass man nie 100%ige Sicherheit haben kann):
Beim SPEICHERN von Eingaben sollte real_escape_string + typ-prüfung der werte (Zahlen, Buchstaben, evtl. Sonderzeichen bzw was man eben erwartet) genügen, um auf der DB-Ebene auf sicherer Seite zu sein?

Beim Auslesen von Daten muss ich dann entscheiden, was angezeigt werden darf, d.h. z.b. strip_tags anwenden, oder eben htmlspecialchars etc.?

Was ich z.B. aber nicht weiß: Muss ich tatsächlich real_escape_string über jeden Wert einzeln laufen lassen oder kann ich den ganzen SQL-String, wenn er fertig ist, escapen lassen?

Und noch zu deinem Generalisierungsansatz: hier hab ich doch aber genauso viel wenn nicht noch mehr SChreibaufwand als wenn ich jeden Wert prüfe? ich müsste ja dann auch für jede Eingabemaske ein neues Array anlegen oder?

@Supermax: Mich würde hier interessieren, wie du mit hilfe deiner Funktionen entscheiden kannst, bei welchem der im Array übergebenen Parameter escaping benötigt wird und wo nicht? Ist das im Array als Parameter mit übergeben? Dann wird das Array doch ziemlich unübersichtlich oder? Kannst du mal ein Codebeispiel z.b. eines Aufrufes zeigen?
0 Punkte
Beantwortet von son_quatsch Experte (5.3k Punkte)
Beim SPEICHERN von Eingaben sollte real_escape_string + typ-prüfung der werte (Zahlen, Buchstaben, evtl. Sonderzeichen bzw was man eben erwartet) genügen, um auf der DB-Ebene auf sicherer Seite zu sein?
Ja. Und sogar eins davon reicht meist - denn wenn du nur eine Zahl hast (auch Gleitkomma mit Exponent und pipapo), dann enthalten die keine für SQL speziellen Zeichen und müssen auch nicht escaped werden.

Beim Auslesen von Daten muss ich dann entscheiden, was angezeigt werden darf, d.h. z.b. strip_tags anwenden, oder eben htmlspecialchars etc.?
Ja. Soll das HTML als solches vom Browser interpretiert werden? Dann unverändert lassen. Soll das HTML als Code im Browser dargestellt werden (z.B. als Anleitung), dann htmlspecialchars (damit aus "<div>" dann richtigerweise "&lt;div&gt;" wird, damit es eben nicht als HTML selbst vom Browser interpretiert wird). Soll keins von beidem der Fall sein? Dann strip_tags() - wobei ich mir allerdings kein Szenario überlegen kann, in welchem man HTML-Tags mutwillig entfernen will - man ist doch besser aufgehoben, entsprechenden Text mit htmlspecialchars für HTML zu escapen.

Was ich z.B. aber nicht weiß: Muss ich tatsächlich real_escape_string über jeden Wert einzeln laufen lassen oder kann ich den ganzen SQL-String, wenn er fertig ist, escapen lassen?
Nicht verstanden. Escapen bedeutet, dass man alle speziellen Sachen für einen Bezug entsprechend entschärft, damit sie keine spezielle Funktion haben.

Unter SQL sind spezielle Zeichen z.B. das Semikolon - damit wird ein Kommando vom nächsten getrennt. Um dieses aber selbst als (nicht zu interpretierendes) Zeichen zu verwenden, kann man einen Backslash davorsetzen. Genauso das Hochkomma - das wird verwendet, um einen Text zu beginnen. Braucht man im Text selbst jedoch auch ein Hochkomma, dann kann man das ja nicht verwenden, weil man dadurch den Text als solches wieder abschließt. Aber mit einem Backslash vor dem Hochkomma wird das Hochkomma selbst als Textzeichen gewertet, statt als Textanfang oder -ende.

Unter HTML sind spezielle Zeichen z.B. das "kleiner als" (spitze Klammer auf) und das "größer als" (spitze Klammer zu) - damit wird ein Tag begonnen und wieder geschlossen. Will man aber diese Zeichen selbst in HTML darstellen, muss man eine Entität stattdessen verwenden. Eine Entität fängt immer mit einem Kaufmanns-Und an und endet mit einem Semikolon - z.B. &lt; für "kleiner als" und &gt; für "größer als". Und was passiert, wenn wir ein Kaufmanns-Und in HTML darstellen wollen? Können wir so blank ja nicht schreiben, weil das der Beginn für eine Entität darstellt. Kurz: auch dafür gibt es selbst eine Entität, nämlich &amp; - damit wird in HTML ein & dargestellt.

Hast du jetzt den Sinn von escapen verstanden? Und warum man es nicht global für alles machen kann/muss? Und warum es mehr als unsinnig wäre, ein komplettes SQL-Kommando zu escapen?

Und noch zu deinem Generalisierungsansatz: hier hab ich doch aber genauso viel wenn nicht noch mehr SChreibaufwand als wenn ich jeden Wert prüfe? ich müsste ja dann auch für jede Eingabemaske ein neues Array anlegen oder?
Nein - wenn du z.B. in drei deiner Eingabemasken dieselben Werte verwendest (wie z.B. "Alter" oder "Name"), dann kannst du jene Felder genauso nennen und brauchst sie entsprechend nur einmal definieren. Der Aufwand könnte für dich jetzt sogar höher aussehen. Bedenke jedoch, dass das eine weit saubere Implementierung ist. Sollte sich später mal etwas ändern (du erweiterst die Maximallänge beim Namen von 50 auf 150 Zeichen oder für eine Telefonnummer dürfen nun doch auch andere Zeichen eingegeben werden als nur Ziffern und Leerzeichen), dann gibt es lediglich eine Stelle, an der du etwas abändern musst. Bei deiner "Methode" musst du sämtliche betroffenen Stellen im Code finden und abändern. Und die Übersichtlichkeit wolltest du doch auch bedenken.
0 Punkte
Beantwortet von
Ok, das mit dem escapen leuchtet mir jetzt ein.

Nur die Geschichte mit dem Generalisierungsansatz ist irgendwie noch nicht so ganz drin. Also wenn ich das richtig verstanden habe, sollte ich an einer zentralen Stelle alle möglichen Datenbank-Werte, in z.B. einem Array, speichern und darauf dann zugreifen? Das müsste ich ja dann für jede Tabelle extra machen, da ich ja in Tabelle A ein Feld "name" haben kann und in Tabelle B ebenfalls...

Und nohc eine andere Frage hab ich, ich denke die passt hier auch mit rein. Vielleicht erübrigt sich dann alles andere auch... Muss bzw. sollte ich bei größeren Projekten ein Framework wie z.B. Zend benutzen? Ich bin momentan eigentlich immer ganz gut mit meiner Template-Engine gefahren, aber nun wurde mir gesagt, dass sowas nur Sinn macht, wenn man die Software vermarkten will, damit Benutzer die Templates für sich anpassen können. Aber eigentlich bin ich ganz zufrieden damit (ich nutze XTemplate)
0 Punkte
Beantwortet von supermax Experte (4.8k Punkte)
Ein Framework (Zend, Symfony, CakePHP, Kohana usw.) zahlt sich eigentlich nur bei größeren Projekten aus, insbesondere wenn mehrere Leute gleichzeitig daran arbeiten, da der Overhead doch beträchtlich sein kann.

Die meisten Frameworks erlauben es aber, einzelne Komponenten (z.B. Mailer, Template-Engine, Caching-Engine) alleinstehend oder mit nur geringen Abhängigkeiten vom Rest des Frameworks einzusetzen.

Vom Benutzer eingegebene Werte sollten dort gefiltert bzw. escaped werden, wo es Sinn macht, eine generelle Aussage läßt sich da nicht einfach treffen, ohne das jeweilige Projekt zu kennen.

Wenn du z.B. eine bestimmte Texteingabe nie als HTML ausgeben willst, kann das strip_tags() ruhig schon vor dem Eintragen in die DB erfolgen, das erspart den Funktionsaufruf bei jeder Ausgabe. Natürlich muß der Wert trotzdem noch mit mysql_real_escape_string() behandelt werden.

Wenn du weißt, daß ein bestimmtes Feld nur ganze Zahlen oder nur Fließkommazahlen speichert, kannst du aber statt mysql_real_escape_string() auch intval() bzw. floatval() verwenden.

Auch die Filterfunktionen von PHP können sehr nützlich sein.
0 Punkte
Beantwortet von son_quatsch Experte (5.3k Punkte)
Also wenn ich das richtig verstanden habe, sollte ich an einer zentralen Stelle alle möglichen Datenbank-Werte, in z.B. einem Array, speichern und darauf dann zugreifen? Das müsste ich ja dann für jede Tabelle extra machen, da ich ja in Tabelle A ein Feld "name" haben kann und in Tabelle B ebenfalls...
Unkompliziert: Ja.

Differenziert betrachtet: wenn in allen Tabellen die Spalten identisch sind (sollte eigentlich nie eintreten, weil dann dein Normalisierungsmodell fehlschlägt), dann reicht eine Definition. Gibt es mehrere Tabellen mit demselben Spaltennamen aber unterschiedlichen Definitionen (z.B. die eine hat maximal 140 Zeichen, die andere nur 50 - oder die eine ist ein VARCHAR, die andere ein TEXT), dann musst du auch jeweils unterschiedliche Defintionen haben (mit unterschiedlichen Namen, wie z.B. "nameeins" und "namezwei").

Supermax' filter-Funktionen von PHP sind auch ein Ansatz. Auch hier muss man sich aber mit deren Funktionsweise erst vertraut machen, sonst bekommt man mitunter unerwartete Ergebnisse.
0 Punkte
Beantwortet von
Also kann ich doch festhalten, dass nicht zwangsläufig ein Projekt, was ohne Framework auskommt, gleich schlecht ist? Also ich geh jetzt von dem Fall aus, dass ich versuche, möglichst den Anwendungs-Code vom HTML-Code zu trennen (daher hab ich bisher immer ein Template-System verwendet). Da ich immer allein an meinen Projekten arbeite, kann ich mir das Framework also eigentlich sparen oder?

Zumindest habt ihr mir bei meiner eigentlich Frage gut weitergeholfen. Dafür schon mal danke!

Eigentlich hab ich auch noch ein paar Fragen zur OOP, aber dafür mache ich lieber nen neuen Thread auf.. :-)
0 Punkte
Beantwortet von son_quatsch Experte (5.3k Punkte)
Sehr schön - tu das (letztere) :-)

Ich z.B. verzichte möglichst auf Frameworks. Wenn die Projekte überschaubar bleiben, dann würden Frameworks die nur unnötig aufblähen - immerhin muss deren gesamter PHP-Code auch immer ausgeführt werden. Außerdem bin ich mir fast immer unsicher, wie denn jene Funktionalitäten der Frameworks tatsächlich umgesetzt sind - genügen sie meinen Ansprüchen oder weisen sie genau die Schwachstellen auf, die ich durch Detailrecherche kenne und vermeide? Frameworks kann man normalerweise auch nicht an spezifischen Stellen ändern. Und möchte man es mal aktualisieren, müssen selbst alle tatsächlich geänderten Stellen berücksichtigt und wieder neu getestet werden.

Ja - mit einem Framework wird vieles einfacher und man kommt schneller zu Resultaten. Die Kehrseite der Medaille sind Details, für die sich in der heutigen Zeit die wenigsten interessieren - tun sich hier Fragen und Probleme auf, kostet deren Beantwortung / -hebung mehr Zeit, die subjektiv als "Bremse" empfunden wird. Je mehr Detailfragen auftauchen, die das Framework unbeachtet lässt oder gar falsch beantwortet, desto mehr sollte man in Erwägung ziehen, sein eigenes Framework zu bauen.
0 Punkte
Beantwortet von
Vielen Dank für die tollen Antworten!
...