User-Input validieren

Igäl

Erfahrenes Mitglied
Werte Freunde gepflegter Codezeilen

Ich habe mich wieder einmal an meine Scripts gewagt und versuche wieder etwas reinzukommen. Mit PHP5 hat es einige Neuerungen gegeben, denen ich mich nun stellen muss. Nun bin ich dabei meine alten Scripts zur Überprüfung von User-Input zu überarbeiten. Meine Frage an euch ist nun die Folgende:

Wie habt ihr die neue Funktion filter_input in derartige Scripts integriert? Verzichtet ihr in den entsprechenden Funktionen nun auf type casting oder settype() und dergleichen?

Für die Überprüfung von Strings hat sich nichts geändert, richtig? Jedenfalls habe ich hierfür keinen allgemeinen Filter gefunden. Das würde ich nun weiterhin mit is_string() / settype() oder dem "(string)"-caster erledigen. Wie seht ihr das?

Besten Dank für die Denkanstösse.

Es grüsst,
de Igäl
 
Hi

nicht vergessen, dass PHP schon bei Version 7 ist :)

Meine persönliche Meinung dazu: Regex, optional mit manueller Charsetbehandlung. Alles Andere hat mir zu viele Probleme und Spezialfälle, die man sich merken muss und vergessen könnte. zB.

Wie reagiert welche Methode auf...
a) nicht existierende Variablen? (Manche akzeptieren die)
b) leere Strings?
c) Spezialwerte wie 0, "true" usw.? (Für mich ist zB. true für die meisten Anwendungsfälle kein Integer)
d) Werte wie 1e3 (bedeutet 1000), 0xff (255), --1 (1) usw.: Akzeptieren ja/nein hängt sehr vom jeweiligen Sinn ab.

Und noch schlimmer, der Umgang mit Zeichensätzen, Unicodenormalisierung usw. ist in so ziemlich allen fertigen PHP-Funktionen nicht mal annähernd verlässlich oder wenigstens klar geregelt.

usw.
 
Zuletzt bearbeitet:
Naja bis die Provider auf PHP7 umstellen, bin ich alt. Was ich schon gewartet habe, bis ich endlich mit PHP5 sauber OOP betreiben konnte, weil die Provider so lange an 4.x festgehalten haben... Ich konzentrier mich vorderhand noch auf PHP5 mit den Neuerungen der jüngsten Releases im Hinterkopf.

Meinst du mit manueller Charsetbehandlung, dass du eingehende Zeichen selber prüfst und entsprechend umwandelst? So à la, suche im String nach allen "ä" und wandle sie um in "ä" oder dergleichen?

Mit regulären Ausdrücken zu prüfen passt, das werde ich wohl in die Konzeption aufnehmen.

Was wäre der Vorschlag wenn es aber vorderhand lediglich darum ginge XSS zu verhindern und die superglobalen $_GET und $_POST durchzuprüfen um schädlichen Input abzufangen? Also wenn bei mir "1E2" immer 100 ist und chars immer chars bleiben und nicht als ASCII-Codes daherkommen. Wird dann eher type-casting / settype() oder die Arbeit mit filter_input empfohlen?
 
Das ü meinte ich zwar nicht direkt, aber zuerst dazu: Im Idealfall sollte das überhaupt nicht mehr vorkommen bzw. nötig sein, nämlich wenn man alles auf UTF8 (zB.) umgestellt hat. ("Alles" = HTML-Metatags, HTTP-Header, PHP-Dateien, DB-Verbindungseinstellungen, DB-Tabellen/Spalten, ... und leider teilweise auch PHP-Code, weil PHP in dem Bereich eben so schlecht ist. Die anderen Sachen (außer dem Code eben) gehen relativ schnell.

Die Hauptprobleme mit den Zeichensätzen sind:
a) Was per GET/POST reinkommt, was mit echo wieder rausgeht, Variableninhalte in PHP, und fixe Stringliterale wie "HelloWorld" im Code; diese 4 Sachen haben potentiell 4 verschiedene Zeichensätze. Zusätzlich kann es sich je nach Server, Einstellungen, verwendetem Codeeditor, Clientbrowser usw. beliebig ändern.
b) Die meisten "normalen" PHP-Funktionen haben keine Ahnung von Zeichensätzen, nur von Bytes (dokumenitert ist das natürlich nirgends, PHP eben). Einige Funktionen, wo der Inhalt von Strings wichtig ist, funktionieren daher je nach Zeichensatz einfach nicht (gar nicht, oder nicht immer, oder nur mit Sicherheitslücken). Für manche Fälle gibt es Ersatzfunktionen, aber längt nicht für alles.
c) Die meisten PHP-"Programmierer" haben keine Ahnung von irgendwas, inklusive die Schreiber der Doku, und komplizierte Sachen wie Unicodenormalisierung machen die Sache nicht besser.

Was das für die Praxis bedeutet?
Normalerweise triviale Sachen wie
* zwei Strings vergleichen
* ermitteln wieviel Buchstaben ein String hat
* prüfen ob ein GET-Parameter eine Zahl ist
...
werden dadurch gar nicht mehr so trivial. Manchmal ist es akzeptabel, dass der Code für zB. 0.1% der möglichen Eingaben nicht funktioniert, aber manchmal eben nicht (funktional und/oder Sicherheit), und ein besserer Code kann relativ lang und komplex werden.


Wie viele normale lesbare Buchstaben hat der String "ä" (ohne Anführungszeichen)?
Jeder Mensch würde sagen 1. PHP sagt je nach Situation und Code (normalerweise) irgendwas zwischen 1 und 14, und wenn man es als Ersteller der Eingabe unbedingt will kann man "jede" beliebige Länge erzeugen. Etwas, das wie ein einzelnes ä ausschaut, kann in strlen auch eine Million ergeben. Wirklich alles geht. (Direkt ist das eigentlich die Schuld vom Unicode-Konsortium, nicht von PHP, aber PHP fehlt eben einiges an Funktionen und guter Doku)

Und natürlich sind die 14 normalen ä (und auch alle anderen) unterschiedlich, wenn sie mit == oder === verglichen werden. Obwohl alles nur ä ist.

Oder anderes Beispiel, kennst du schon Text, der höher als die Zeile ist? Hier ein ä mit Ergänzungen:

.
.
.
.

ä͌̌͒̍̀͑ͮ͆ͣͤ̆̌̌

.
.
.
.
Ja, das ist wirklich nur ein Buchstabe.

...

Zu XSS: Explizit prüfen wurd ich Ausgaben für den Zweck gar nicht, sondern einfach immer zB. htmlspecialchars verwenden.

.
.
.
t̲͈̩͑ͮͫ̆̐̿͝u̺̱͕̳ͩͤt͙̺͖͗ͨo̙͉̣͔̯̐ͤr̢̝̩̯̒ͪͩi̘͇̟͎̲ͫ̈ͤͧ̈̕a͙̼̹ͨ̒ͭ̍ͣ̚l̄ͪ̓̒ş̼̮̗̿ͭ̂̓ͨͧ.̐̊̒̎ͬͬ̈͜d̵͈͖̝͉̗ͪͭ̏ͭ̽̀̍è̠̝̹̫ :p
 
Zuletzt bearbeitet:
Igäl hat gesagt.:
Wie habt ihr die neue Funktion filter_input in derartige Scripts integriert? Verzichtet ihr in den entsprechenden Funktionen nun auf type casting oder settype() und dergleichen?

Ich bin, wie sheel, ebenfalls kein großer Freund der filter_*-Funktionen. (Für weitere Begründungen siehe zuletzt hier ab etwa Post #12, auch wenn ich mich da in ein, zwei Aspekten vielleicht ein wenig verrannt hatte.) Fairerweise muss man aber sagen, dass einige Leute sie ganz gern einsetzen. Ich persönlich glaube aber, dass die Nutzer damit gewissermaßen „unsaubere“ Schnittstellen in Kauf nehmen (vgl. sheels Ausführungen so Sonderfällen und Details der filter_*-Funktionen hier im Thread).

Ich bin auch mehr ein Fan von regulären Ausdrücken, wenn es etwa darum geht, Zahlen zu erkennen. Ich nutze aus Gründen der Einfachheit aber häufig auch lediglich Typecasts, auch wenn die es beispielsweise erlauben, dass eine Eingabe wie "10foo" als int(10) interpretiert wird.

Ein typische Zeile mit Typecast wäre:

PHP:
$intParam = (isset($_POST['n']) && !is_array($_POST['n']))
        ? (int) $_POST['n']
        : 0;

Wobei int(0) hier der Default-Wert ist, wenn der Parameter nicht oder fehlerhaft gesetzt ist.

Der Ansatz stellt verlässlich sicher, dass ich in der Anwendung zumindest den korrekten Datentypen habe. Eingaben wie "10foo" werden zwar als int(10) ausgewertet, was nicht so ganz schick ist, aber na ja.

* * *​

Ich prüfe (leider) selten bis nie, ob ein String als UTF-8 NFC vorliegt, obwohl das sinnvoll wäre. Das liegt allerdings auch daran, dass ich oft an Third-Party-Code arbeite, der in der Regel ganz andere Probleme hat. Aber das ist sicher ein Bereich, in dem wir als PHP-Community insgesamt noch Luft nach oben haben. Wobei das meines Erachtens alles nicht so einfach ist, ohne wirklich eine Utf8NfcString-PHP-Klasse oder so zu haben. Und das ist nicht wirklich absehbar. Ich glaube, dass es schwierig ist, da den sweet spot (wann und wo welcher Check) zu finden und dann auch noch immer zuverlässig zu treffen.

Ist gerade die UTF-8-NFC-Sache in anderen (Script-)Sprachen besser gelöst? Hat da jemand Vergleichswerte?

Igäl hat gesagt.:
Naja bis die Provider auf PHP7 umstellen, bin ich alt. Was ich schon gewartet habe, bis ich endlich mit PHP5 sauber OOP betreiben konnte, weil die Provider so lange an 4.x festgehalten haben... Ich konzentrier mich vorderhand noch auf PHP5 mit den Neuerungen der jüngsten Releases im Hinterkopf.

Hmm… Da regt sich in mir mittlerer Widerspruch. ;) Ich habe auf Anhieb keinen Anbieter gefunden, der (zumindest bei normalem Webspace) noch kein PHP 7 anbietet. Wenn du jetzt erst auf PHP 5 umsteigst, bist du – wohlwollend gesagt – ganz ganz locker 6 Jahre hinten dran. Eher erheblich mehr. Der offizielle PHP-Support für 4.x lief am 7. August 2008 aus. Mag sein, dass da eine Linux-Distribution noch einige Jahre Patches nachgepflegt hat, aber viel mehr als 2 Jahre räume ich da auch nicht ein. Die erste PHP-5-Version ist vor 12 Jahren erschienen.

Aus Sicht des gesamten PHP-Ökosystems finde ich es aber nicht falsch, zumindest noch 1-2 Jahre zu PHP 5.4+ oder PHP 5.5+ kompatibel zu sein. Dann laufen LTS-Distributionen von Debian und Ubuntu so langsam aus, die noch die entsprechenden Versionen enthalten. Das ist aber ein gänzlich anderes Argument als jenes, dass Hosting-Anbieter kein PHP 7 anbieten würden.

Wie auch immer: PHP-5-Code, der auf zwei, drei Kleinigkeiten achtet, ist vollständig aufwärtskompatibel. Es ist im Grunde PHP-7-Code, der nur gewisse Features nicht nutzt.

Igäl hat gesagt.:
Was wäre der Vorschlag wenn es aber vorderhand lediglich darum ginge XSS zu verhindern und die superglobalen $_GET und $_POST durchzuprüfen um schädlichen Input abzufangen?

So was hat im Grunde nichts bei der Validierung von Eingaben verloren, weil die Anwendung an der Stelle noch gar nicht wissen können sollte, in welchem Kontext die Daten später verwendet werden. Sicherung gegen XSS und dergleichen erledigt man sinnvollerweise erst an dem Punkt, an dem die Daten tatsächlich in HTML-Code oder SQL-Queries und dergleichen geschrieben werden.

(Edit: Natürlich nur gegen die Dinge escapen, die für den jeweiligen Kontext relevant sind. Beim Eintragen von Daten in SQL-Queries muss nicht gegen XSS abgesichert werden und umgekehrt nicht beim Eintragen in HTML gegen SQL-Injections. http://wiki.selfhtml.org/wiki/PHP/Anwendung_und_Praxis/Kontextwechsel)

sheel hat gesagt.:
Das ü meinte ich zwar nicht direkt, aber zuerst dazu: Im Idealfall sollte das überhaupt nicht mehr vorkommen bzw. nötig sein, nämlich wenn man alles auf UTF8 (zB.) umgestellt hat.

Noch mal betont: Der Idealfall ist hier sogar auch der absolute Regelfall. Ein maskierter Umlaut im HTML-Code deutet mit an Sicherheit grenzender Wahrscheinlichkeit auf einen falschen Umgang mit Zeichenkodierungen hin. Es gibt nur sehr esoterische Anwendungsfälle, in denen ein ü vielleicht mal Sinn ergibt. Ich persönlich kenne keinen.

sheel hat gesagt.:
b) Die meisten "normalen" PHP-Funktionen haben keine Ahnung von Zeichensätzen, nur von Bytes (dokumenitert ist das natürlich nirgends, PHP eben). Einige Funktionen, wo der Inhalt von Strings wichtig ist, funktionieren daher je nach Zeichensatz einfach nicht (gar nicht, oder nicht immer, oder nur mit Sicherheitslücken). Für manche Fälle gibt es Ersatzfunktionen, aber längt nicht für alles.

Das liegt auch daran, dass viele PHP-Funktionen mehr oder weniger Mappings zu entsprechenden C-Funktionen sind. Ich stimme aber zu, dass das nicht optimal dokumentiert ist. Andererseits ist es aus Nutzersicht auch unrealistisch, anzunehmen, dass die Funktionen mit dem Zeichensatz, den man gerade will, arbeiten.

Grundsätzlich ist es sehr gut möglich, mit PHP und den mb_*-Funktionen mit UTF-8-Strings zu arbeiten.

sheel hat gesagt.:
c) Die meisten PHP-"Programmierer" haben keine Ahnung von irgendwas

Um es gesagt zu haben: Das ist kein Alleinstellungsmerkmal von PHP.

sheel hat gesagt.:
Was das für die Praxis bedeutet?
Normalerweise triviale Sachen wie
* zwei Strings vergleichen
* ermitteln wieviel Buchstaben ein String hat
* prüfen ob ein GET-Parameter eine Zahl ist
...
werden dadurch gar nicht mehr so trivial.

Das liegt aber auch nur begrenzt an der Sprache.

sheel hat gesagt.:
Wie viele normale lesbare Buchstaben hat der String "ä" (ohne Anführungszeichen)?
Jeder Mensch würde sagen 1. PHP sagt je nach Situation und Code (normalerweise) irgendwas zwischen 1 und 14, und wenn man es als Ersteller der Eingabe unbedingt will kann man "jede" beliebige Länge erzeugen. Etwas, das wie ein einzelnes ä ausschaut, kann in strlen auch eine Million ergeben. Wirklich alles geht. (Direkt ist das eigentlich die Schuld vom Unicode-Konsortium, nicht von PHP, aber PHP fehlt eben einiges an Funktionen und guter Doku)

PHP:
var_dump(mb_strlen('ä', 'UTF-8')); // int(1), wenn das "ä" wirklich UTF-8 ist

Dass man in Unicode seltsame Eingabe-Bytesequenzen konstruieren kann, die vielleicht aussehen wie ein "ä", kann man PHP wirklich nicht vorwerfen. Das ist nichts, was Ottonormalnutzer so schnell mal versehentlich passiert. Da muss man es schon gezielt drauf anlegen. Und dann hat man natürlich auch kein normales "ä" eingetippt, wenn man kein normales "ä" eingetippt hat.

sheel hat gesagt.:
Oder anderes Beispiel, kennst du schon Text, der höher als die Zeile ist?

Das macht den Text halt auch nicht per se zu einer Falscheingabe. Solche Fälle machen Validierung eben allgemein und sprachübergreifend (im doppelten Sinne ;)) schwierig, wenn man es darauf anlegt, sie verhindern zu wollen, ohne mögliche Eingaben zu sehr einzuschränken.
 
Zuletzt bearbeitet:
Ausführliche Antwort evt. später (gähn:) ), nur kurz jetzt:

Natürlich sind paar der Probleme nicht die Schuld von PHP. Teilweise war der vorigen Beitrag auch nur generell als Aufzeigung einiger Zeichensatz-Probleme gedacht.

Was (mich) aber an PHP am meisten stört ist, dass es keine Garantie für irgendwas am inneren Verhalten gibt. Bei zB. C weiß man, alles bezieht sich auf Bytes, punkt aus. Bei PHP auf Linux kann das zB. auch stimmen, während es auf Windows irgendwelche wirren eigenen Konvertierungen macht. Man kann es praktisch nicht 100% richtig machen, weil das Verhalten nirgends dokumentiert+garantiert ist und es sich je nach Version und Umgebung schon auch mal ändern kann (und das, was man kann, wird dadurch nicht einfacher)

...
Jedenfalls, super Beitrag, und volle Zustimmung.
 
Zuletzt bearbeitet:
Das war ja doch sehr ausführlich. Besten Dank dafür.

Ich nehme für mich mit, dass ich, ohne absoluter Profi zu sein und sämtliche Hintergrundroutinen verinnerlicht zu haben, mit 99%iger Sicherheit auskommen muss. Weiter, dass ich die Erwartungen an User-Input mit in reguläre Ausdrücke verbriefe anstelle der PHP-internen Filterfunktionen zu verwenden. Und dass ein brachialer Typecast ohne Weiteres zum Ziel führen kann :)

Danke Leute. Es hat einige Denkanstösse dabei, an welche ich mich (für das Level an Qualität, welches ich mir für meine Scripts vorstelle) heranmachen kann.

Danke euch Zwei hierfür.
 

Neue Beiträge

Zurück