dedlfix: nach Login gültigen Account prüfen

Beitrag lesen

echo $begrüßung;

Das verstehe ich bis heute nicht richtig wann " oder ' oder nix. :(

Wenn zwei Kontexte zusammenkommen, muss man beide eindeutig unterscheiden können. Da ist das SQL-Statement, das (vereinfacht gesagt) Buchstaben und Leerzeichen nutzt, um eine Anweisung zu formulieren. Und da ist ein Wert, der in diese Anweisung eingefügt werden soll, der ebenfalls aus Buchstaben und Leerzeichen besteht. Wie kennzeichnet man nun diesen Wert so, dass er nicht mit dem Anweisungsteil verwechselt werden kann? Man beginnt und beendet ihn mit je einem Anführungszeichen. (Das nennt man Quotieren). Soweit so einfach. Jedoch sind auch Satzzeichen, wie ebenjenes Anführungszeichen mitunter Bestandteil der Daten und dann muss man es so notieren, dass der Statement-Interpreter es nicht als beendendes Anführungszeichen interpretiert. Dafür kommt das Maskieren ins Spiel. Das Anführungszeichen bekommt eine Maske vorgesetzt, so dass es nicht als solches erkannt wird. Wenn der Kontext »String im SQL-Statement« nicht vorliegt, muss auch nicht dafür maskiert werden. (Bei md5($wert) ist der Kontext »Funktionsaufruf«, für den nichts weiter zu beachten ist.)

Dass es nun sowohl " als auch ' gibt, ist dafür gedacht, sich teilweise die Maskierungen zu ersparen, wenn man mehrere Kontexte ineinanderschachtelt. Weiterhin haben in bestimmten Systemen die beiden Zeichen unterschiedliche Auswirkungen auf den Inhalt, was vor der Entscheidung für " oder ' zu beachten ist. PHP behandelt " und ' unterschiedlich, MySQL nicht.

$sql = "SELECT * FROM table WHERE feld='wert'";

In diesem Beispiel müssen die drei Kontexte PHP-String, SQL-Statement und SQL-String mit zwei Quotierzeichen auskommen. Das SQL-Statement verwendet das " nicht, weswegen es als PHP-Quotierzeichen verwendet werden kann. Auch andersrum geschachtelt ist eine Notation in diesem einfachen Beispiel möglich, da keine unter PHP besonders behandelten Dinge (z.B. Variablennamen) vorkommen.

$sql = 'SELECT * FROM table WHERE feld="wert"';

Problematisch wird die Sache erst, wenn »wert« variabel sein soll und Zeichen wie ' oder " enthalten können soll.

$sql = "SELECT * FROM table WHERE feld='$wert'";

Für den Kontext PHP-String brauchen wir den Inhalt von $wert nicht zu betrachten. Der Parser, der den Kontext PHP-String zu beachten hat, ist längst gelaufen, wenn es darum geht, den Inhalt von $wert in den String einzufügen. Aber der Kontext SQL-Statement und darin SQL-String ist noch zu beachten, den löst ja erst später das DBMS auf.

Wir haben nun den Kontext »mit ' eingefasster SQL-String«, und da hat ein " keine Bedeutung. Bleibt also noch, vorkommende ' zu entschärfen. Die Arbeit des Maskierens nimmt die Funktion mysql_real_escape_string() ab. Ihr Aufruf erfolgt ja auch erst zur Laufzeit, wenn der restliche Statement-String bereits geparst in den Tiefen des PHP-Systems vorliegt und den Kontext PHP-String hinter sich gelassen hat, so dass dieser hier ebenfalls nicht beachtet werden muss. Dass die Funktion neben ' auch " maskiert ist in diesem konkreten Fall überflüssig, aber nicht weiter tragisch. Sie muss ja auch für den Fall »mit " eingefasster SQL-String« arbeiten können.

Ergebnis ist der String

SELECT * FROM table WHERE feld='wert mit einem ' drin'

den du dir mit einer Kontrollausgabe ansehen kannst. Auch ein

SELECT * FROM table WHERE feld='wert mit einem ' und einem " drin'

wäre richtig, doch mysql_real_escape_string() erzeugt

SELECT * FROM table WHERE feld='wert mit einem ' und einem " drin'

was ja unproblematisch ist. Auch dann, wenn der SQL-String mit " eingerahmt wäre

SELECT * FROM table WHERE feld="wert mit einem ' und einem " drin"

denn der MySQL-Parser löst ' und " zu ' und " auf, egal wie der String eingefasst war.

Bleibt noch der Fall, wann »nix« zu verwenden ist. Eine Zahl ist eindeutig als solche zu erkennen. Sie beginnt mit einem der Zahl-Zeichen (Ziffern, Vorzeichen, Dezimaltrennzeichen) und endet vor dem nächsten Nicht-Zahl-Zeichen. Die Zahl ist damit eindeutig erkennbar und muss nicht mit Begrenzungszeichen markiert werden.

Wenn aber eine Zahl aus einer Benutzereingabe stammt und nicht sichergestellt wurde, dass wirklich nur eine Zahl vorliegt (sicherstellen kann man das unter PHP beispielsweise mit einem Typecast zu Integer oder mit intval()), dann kann man diese auch wie einen String behandeln und so notieren, also das volle Programm mit Anführungszeichen und Maskierungsfunktionsaufruf. Eine falsche Zahleneingabe wird dadurch zwar nicht korrigiert und liefert gegebenenfalls fachliche Fehler, kann so aber keinen Schaden durch SQL-Injektion anrichten. Diese Aussage gilt zumindest für MySQL. Andere DBMS verlangen eine ordentliche Zahl-Notation bei Feldern mit nummerischem Typ. Hier muss man dann in der Programmumgebung für eine ausreichende Prüfung und Konvertierung sorgen.

Die Quotier- und Maskier-Problematik für SQL-Statements löst sich übrigens beim Einsatz von Prepared Statements in Luft auf, weil mit dieser Technik Statement und Werte getrennte Wege zum DBMS gehen und keine Kontextschachtelung zu berücksichtigen ist. Doch das ist ein anderes Thema und eigentlich nur ein Nebeneffekt von P.S.

Wo kann ich mich dazu besser belesen?

Im Handbuch zur Syntax des jeweiligen System, speziell im Bereich der von String-Notation handelt.

Da ein Handbuch meist nur (s)einen (eigenen) Kontext behandelt, in der Praxis aber Kontexte geschachtelt auftreten, muss man berücksichtigen, wie diese aufgelöst werden können. Am Anfang hat man einen String. Kontext 1 verlangt die Notation des Zeichens LF als \n. Kontext 2 sieht nun die beiden Zeichen \ und n. Aus Sicht von Kontext 2 sind beide getrennt zu betrachten und entsprechend zu notieren. Das \ wird zu \ und das n bleibt wie es ist, weil es kein Sonderzeichen ist. Für Kontext 3 muss man \ \ n nun als \ \ n notieren.

Beim Auflösen sieht das System, das den Kontext 3 behandelt, \ \ n und macht daraus \n. System 2 löst \n zu \n auf uns System 1 macht das \n wieder zum LF.

Wenn die Systeme unterschiedliche Zeichen mit Sonderbedeutung betrachten, muss auch nur für das jeweilige System/Kontext die entsprechende Umschreibung verwendet werden. Wenn Kontext 2 \ nicht als Sonderzeichen interpretiert, dafür aber ein <, so muss für den Kontext 2 das \ so bleiben und < als &lt; notiert werden (angenommen, die Vorschrift für Kontext 2 sieht das so vor). Für Kontext 3 macht man das eine \ zu \ und lässt die Zeichenfolge &lt; unverändert.

Im Prinzip ist es also immer nur wichtig, den jeweiligen Kontext zu identifizieren und jedes Zeichen einzeln für diesen zu betrachten und gegebenenfalls zu behandeln.

echo "$verabschiedung $name";