getRequestBody-Methode gibt keine Formular-Werte aus
borisbaer
- oop
- php
Hallo zusammen,
ich habe ein Problem mit meiner getBody()-Methode:
public static function getBody(): array
{
$body = [];
if ( $_SERVER['REQUEST_METHOD'] === 'GET' )
foreach ( $_POST as $key => $value )
$body[$key] = filter_input( INPUT_GET, $key, FILTER_SANITIZE_SPECIAL_CHARS );
if ( $_SERVER['REQUEST_METHOD'] === 'POST' )
foreach ( $_POST as $key => $value )
$body[$key] = filter_input( INPUT_POST, $key, FILTER_SANITIZE_SPECIAL_CHARS );
return $body;
}
Diese soll dazu dienen, http-Anfragen von bösartiger Eingabe zu befreien.
Wenn ich jedoch in einer meiner Controller-Klassen diese getBody()-Methode abrufe ($body = Controller::getBody();
), dann bekomme ich nur die Array-Keys geliefert, die entsprechenden Values bleiben leer.
Ich weiß nicht, warum das so ist, denn bei print_r( $_POST )
erscheinen sowohl die Keys als auch die Values. Außerdem funktioniert die getBody()-Methode bei einem anderen Formular, und zwar bei dem Formular zur Erstellung eines neuen Benutzerkontos. Ich habe keine Idee, woran es liegen könnte.
Stimmt was mit dem Input nicht?
Grüße
Boris
Hallo,
public static function getBody(): array { $body = []; if ( $_SERVER['REQUEST_METHOD'] === 'GET' ) foreach ( $_POST as $key => $value ) $body[$key] = filter_input( INPUT_GET, $key, FILTER_SANITIZE_SPECIAL_CHARS );
selbst ins Knie gesschossen? Wieso greifst du bei einem GET-Request auf die Keys und Values in $_POST zu? Das muss ja schiefgehen.
By the way: Warum machst du immer doppelte Zeilenumbrüche im Code? Übersichtlicher wird's dadurch nicht.
Diese soll dazu dienen, http-Anfragen von bösartiger Eingabe zu befreien.
Ja, und die Absicht ist löblich, aber der Ansatz komplett falsch. Es gibt keine per se bösartigen Eingaben. Die kontextgerechte Aufbereitung (meist durch Maskierung oder Codierung) sollte da erfolgen, wo die Daten bei der Verarbeitung in einen anderen Kontext gelangen, wo bestimmte Zeichen plötzlich Schaden anrichten könnten. Einfach bei der Ankunft alles plattzubügeln, ist kontraproduktiv.
Wenn ich jedoch in einer meiner Controller-Klassen diese getBody()-Methode abrufe (
$body = Controller::getBody();
), dann bekomme ich nur die Array-Keys geliefert, die entsprechenden Values bleiben leer.
Bei welcher Request-Methode? GET oder POST? Weil ... für POST sieht's ja korrekt aus. Abgesehen von der wenig sinnvollen Keule filter_input(), deren Sinnhaftigkeit ich generell in Frage stelle.
Ich weiß nicht, warum das so ist, denn bei
print_r( $_POST )
erscheinen sowohl die Keys als auch die Values.
Also geht's um einen POST-Request?
Einen schönen Tag noch
Martin
Hallo Martin,
selbst ins Knie gesschossen? Wieso greifst du bei einem GET-Request auf die Keys und Values in $_POST zu? Das muss ja schiefgehen.
ach, Mist, das sollte da gar nicht stehen. In meiner Verwirrung hatte ich das Ganze mal an der Stelle mit $_GET
ausprobiert – nur, um zu sehen, was passieren würde. Aber im ursprünglichen Code stand $_POST
. Geht leider trotzdem nicht.
By the way: Warum machst du immer doppelte Zeilenumbrüche im Code? Übersichtlicher wird's dadurch nicht.
Für mich an meinem Monitor ist das viel lesbarer. Ich kann den Code viel schneller visuell erfassen. Wahrscheinlich habe ich da einfach eine komische Wahrnehmung. 🤷♂️
Ja, und die Absicht ist löblich, aber der Ansatz komplett falsch. Es gibt keine per se bösartigen Eingaben. Die kontextgerechte Aufbereitung (meist durch Maskierung oder Codierung) sollte da erfolgen, wo die Daten bei der Verarbeitung in einen anderen Kontext gelangen, wo bestimmte Zeichen plötzlich Schaden anrichten könnten. Einfach bei der Ankunft alles plattzubügeln, ist kontraproduktiv.
Hmm, ich habe diese Vorgehensweise aus einem Tutorial zum Schreiben eines MVC-Frameworks. Ich muss mich in Bezug auf SQL Injections weiterbilden.
Bei welcher Request-Methode? GET oder POST? Weil ... für POST sieht's ja korrekt aus. Abgesehen von der wenig sinnvollen Keule filter_input(), deren Sinnhaftigkeit ich generell in Frage stelle.
Für POST-Requests. Wäre es also sinnvoller z.B. so was hier zu machen:
function input_filter($data) {
$data = trim( $data );
$data = stripslashes( $data );
$data = htmlspecialchars( $data );
return $data;
}
Danke schon mal bis hierhin.
Grüße
Boris
Hallo Boris,
By the way: Warum machst du immer doppelte Zeilenumbrüche im Code? Übersichtlicher wird's dadurch nicht.
Für mich an meinem Monitor ist das viel lesbarer. Ich kann den Code viel schneller visuell erfassen. Wahrscheinlich habe ich da einfach eine komische Wahrnehmung. 🤷♂️
okay, dann ticke ich da anders.
Wäre es also sinnvoller z.B. so was hier zu machen:
function input_filter($data) { $data = trim( $data ); $data = stripslashes( $data ); $data = htmlspecialchars( $data ); return $data; }
Nein! Übernimm Eingangsdaten erstmal so, wie sie sind. Das passt schon. Erst beim Übertragen in andere Kontexte oder Formate (SQL, HTML, JSON, whatever) wird der Wert behandelt. Und zwar speziell auf den Zielkontext angepasst. Für SQL brauchst du eine andere Aufbereitung als für HTML, fürs Speichern in eine Textdatei vielleicht sogar überhaupt keine. Aber bitte keine Pauschalbehandlung unabhängig vom weiteren Verarbeitungsweg.
Einen schönen Tag noch
Martin
Wäre es also sinnvoller z.B. so was hier zu machen:
function input_filter($data) { $data = trim( $data ); $data = stripslashes( $data ); $data = htmlspecialchars( $data ); return $data; }
Nein! Übernimm Eingangsdaten erstmal so, wie sie sind. Das passt schon. Erst beim Übertragen in andere Kontexte oder Formate (SQL, HTML, JSON, whatever) wird der Wert behandelt. Und zwar speziell auf den Zielkontext angepasst. Für SQL brauchst du eine andere Aufbereitung als für HTML, fürs Speichern in eine Textdatei vielleicht sogar überhaupt keine. Aber bitte keine Pauschalbehandlung unabhängig vom weiteren Verarbeitungsweg.
Okay, ich verstehe. Jeder Kontextwechsel braucht (oder eben auch nicht) ein bestimmte Aufbereitung der Daten. Aber POST-Werte, die man in eine Datenbank übertragen möchte, müssen doch stets irgendwie gefiltert werden, oder?
Hi,
Nein! Übernimm Eingangsdaten erstmal so, wie sie sind. Das passt schon. Erst beim Übertragen in andere Kontexte oder Formate (SQL, HTML, JSON, whatever) wird der Wert behandelt. Und zwar speziell auf den Zielkontext angepasst. Für SQL brauchst du eine andere Aufbereitung als für HTML, fürs Speichern in eine Textdatei vielleicht sogar überhaupt keine. Aber bitte keine Pauschalbehandlung unabhängig vom weiteren Verarbeitungsweg.
Okay, ich verstehe. Jeder Kontextwechsel braucht (oder eben auch nicht) ein bestimmte Aufbereitung der Daten.
ja, genau.
Aber POST-Werte, die man in eine Datenbank übertragen möchte, müssen doch stets irgendwie gefiltert werden, oder?
Ja, aber erst dann, und nicht vorher. Und dann mit mysqli_real_escape_string(). Andere Datenbanken wie z.B. sqlite brauchen möglicherweise wieder andere Maskierungen.
Bei der Verwendung von Prepared Statements ist aber auch das AFAIK nicht mehr erforderlich (man möge mich bitte korrigieren, wenn das Unfug ist).
Einen schönen Tag noch
Martin
Aber POST-Werte, die man in eine Datenbank übertragen möchte, müssen doch stets irgendwie gefiltert werden, oder?
Meinetwegen. Aber nicht so. Die Sanitizer helfen nicht gegen SQL-Injektionen. Da nimm das Zeug, welches die Datenbank-Libary bietet.
Für die Ausgabe bereitest Du den Mist auf, wenn er ausgegeben wird (Dann nämlich kennst Du den Kontext.
Hallo borisbaer,
Aber POST-Werte, die man in eine Datenbank übertragen möchte, müssen doch stets irgendwie gefiltert werden, oder?
Warum sollten sie? Relevant ist nur, dass Du sie so wieder ausgibst, dass sie im Browser keinen Schaden anrichten.
Filterungen sind eher fachlich bedingt. Ein trim() kann sinnvoll sein, das Entfernen von Nicht-ASCII-Zeichen auch (also Codes unter 32). Aber ansonsten Zeichen zu maskieren? Nö. Erst bei der Ausgabe.
Wenn Datenbankfelder kein String sind, sondern ein int oder ein Date, ist es natürlich auch sinnvoll, die Werte dafür vorher zu prüfen, ob sie sinnvoll sind. Das ist Eingabeplausibilisierung. Schlägt die fehl, bekommt der User eine Fehlermeldung. Blind wegmaskieren darfst Du dann nicht.
Rolf
Hallo borisbaer,
FILTER_SANITIZE_SPECIAL_CHARS - HTML-Kodierung von '"<>& und Zeichen mit ASCII-Wert kleiner als 32
Da hat Martin recht, das ist als Eingabefilter nicht per se erforderlich. Für die Übergabe an die DB verwendest Du sowieso prepare um den Kontextwechsel zu kapseln. Und bei der späteren Ausgabe ins HTML verwendest Du htmlspecialchars, das übernimmt diesen Sanitäter-Job ohnehin.
Diese Maskierung in der Eingabe ist sogar schädlich, denn auf einmal steht da ein & wo eigentlich ein & stand, und damit sind ggf. String-Positionen nicht mehr das, was sie mal waren. Oder Vergleiche schlagen fehl, weil Du bspw. testen möchtest, ob jemand "Deathmatch & Co" eingegeben hat. Oder eine DB-Suche scheitert, weil jemand das Spiel "Bumm & Knall" gesucht hat. Du willst diese Zeichen nicht ohne Not maskiert in der DB stehen haben.
Den $_POST im foreach hätte ich vermutlich auch erst nach einer Stunde gesehen. Mann mann mann…
Rolf
Hallo Rolf,
Da hat Martin recht, das ist als Eingabefilter nicht per se erforderlich. Für die Übergabe an die DB verwendest Du sowieso prepare um den Kontextwechsel zu kapseln. Und bei der späteren Ausgabe ins HTML verwendest Du htmlspecialchars, das übernimmt diesen Sanitäter-Job ohnehin.
okay, stimmt eigentlich. Ist also eher unnötig.
Diese Maskierung in der Eingabe ist sogar schädlich, denn auf einmal steht da ein & wo eigentlich ein & stand, und damit sind ggf. String-Positionen nicht mehr das, was sie mal waren. Oder Vergleiche schlagen fehl, weil Du bspw. testen möchtest, ob jemand "Deathmatch & Co" eingegeben hat. Oder eine DB-Suche scheitert, weil jemand das Spiel "Bumm & Knall" gesucht hat. Du willst diese Zeichen nicht ohne Not maskiert in der DB stehen haben.
Das ist in der Tat eine wichtige Überlegung. Daran hatte ich gar nicht gedacht. 👍
Das hier tut es:
<?php
class xTest {
if ( $_SERVER['REQUEST_METHOD'] === 'POST' ) {
foreach ( $_POST as $key => $value ) {
$body[$key] = filter_var( $value, FILTER_SANITIZE_SPECIAL_CHARS );
}
}
return $body;
}
}
$_SERVER['REQUEST_METHOD'] = 'POST';
$_POST['foo']='<Foo>';
$_POST['bar']='<Bar>';
print_r( $_POST );
$body=xTest::getBody();
print_r( $body );
Testet Du mit echtem Request oder mit einer Simulation, in dem Du $_POST
(wie von mir gezeigt) bzw. $_GET
vor dem Test mit Daten befüllst?
Womöglich (sic: womöglich) verwendet das von Dir gezeigte filter_input()
nicht $_POST
oder $_GET
sondern php://input
bzw. HTTP_QUERY_STRING
.
In dem Fall liegt es am Test, nicht am Skript... Mit der Simulation und filter_input()
hatte ich nämlich auch kein Glück.
if ( $_SERVER['REQUEST_METHOD'] === 'GET' )
#...
if ( $_SERVER['REQUEST_METHOD'] === 'POST' )
Ehem. Nur mal so am Rande. Wenn die REQUEST-METHODE === POST ist, dann wird es in $_GET auch Werte geben, die mit dem HTTP_QUERY_STRING reinkamen… Es gibt da kein „Entweder GET oder POST“ sondern, „wenn POST, dann AUCH POST“