Die Empfehlung Nutzereingaben niemals zu vertrauen ist gut und richtig, lässt viele aber anfangen in die falsche Richtung zu denken.
Escaping ist nicht bedingt, sondern kontextsensitiv. Das bedeutet, wenn du Variablen zum Beispiel in einen SQL Kontext einsetzt, dann nutze Escaping für den SQL Kontext. Dabei ist irrelevant, ob in der Variable nun ein Wert steht, auf den der Benutzer direkt Einfluss hat, oder nicht. Du schreibst Daten aus PHP in den Javascript-Kontext, escape sie für den JavaScript Kontext. Escape Daten nicht für den SQL-Kontext wenn du sie in JavaScript schreibst und escape sie nicht für den HTML-Attribut Kontext, wenn du sie in SQL schreibst. Es ist eigentlich nicht schwer zu verstehen, aber insbesondere Anfänger neigen dazu entweder zu wenig oder viel zu escapen (insbesondere einmal für alle Kontexte,
what could possibly go wrong?).
Daten für den richtigen Kontext zu escapen ist nie falsch und zerstört deine Daten nicht. Es sorgt dafür, dass Steuerzeichen wie das ' in O'Mally, die du als Zeichen, aber nicht als Steuerzeichen haben willst, keine Steuerzeichen mehr sind, sondern in dem Kontext als normales Zeichen interpretiert werden. Denk nicht zu viel über "was für ein Query" oder "woher kommen die Daten" nach: du schreibst eine Variable in einen Kontext, escape sie für diesen. Ganz einfach.
Don't trust input: validate it.
Dein Benutzer sendet dir einen Parameter "sort". Prüfe, ob dieser entweder "ASC" oder "DESC" ist. Irgend etwas anderes -> Fehler.
In deiner Variable steht nun also
sicher DESC oder ASC drin und die enthalten ja
offensichtlich keine Steuerzeichen, also ist Escaping nicht notwendig? -> Wrong! Schreibst du die Variable in den SQL Kontext, escape sie für diesen. Nicht bedingt, kontextsensitiv! Code kann sich ändern oder Fehler enthalten.
"Wenn ich alles Escape, brauche ich doch nicht validieren" -> Doch. Du willst das dein Code beim Entgegennehmen der Parameter abbricht und eine Meldung ausgibt, die erklärt, dass der Parameter falsch ist. Es ist Quatsch darauf zu warten, dass die Datenbank sich über einen fehlerhaften Query beschwert und dann nach der Ursache zu suchen. Fail as early as possible!
Es ist Irrglaube zu denken man könne da clever etwas "weg optimieren", das Ergebnis ist nie clever. Die Zeit, die man damit verbringt sich Gründe zu überlegen, warum man etwas nicht tun muss, was man immer tun sollte, ist besser damit genutzt es zu tun ;-)
Mantra: Validate on Input, Escape on Output.
wie realisier ich das nach heutigen standard?
Mir ist nicht so ganz klar, wieso GET Parameter heute als hässlich gelten. Ja, es gibt Dinge, die in der URL nichts verloren haben, zum Beispiel Authorisationsinformationen. Der DAU ist per Definition ahnungslos, postet das als Link in ein Forum und bekommt seinen Account geklaut.
Wenn du & ? und = hässlich findest -> electric.larry hat dafür ja schon eine Lösung genannt.
Grundsätzlich erlauben Parameter in der URL ein Lesezeichen darauf zu setzen. Das kann ein erwünschtes Features sein. Auch lassen sich GET Requests cachen. Das kann ein gigantischer Performancevorteil sein, aber du musst dich mit Cache-Invalidierung beschäftigen. Vielleicht macht es Sinn Caching abzuschalten (z.B. wenn die Performance nicht so relevant ist, dass sie die Kosten für die Entwicklerzeit zum Umsetzen ordentlicher Invalidierung rechtfertigen, oder weil die Entwickler es schlicht nicht hinbekommen, oder weil die Daten sich so häufig ändern, dass es schlicht sinnfrei ist (Trivialbeispiel: Ausgabe der Uhrzeit)). Bei GET musst du das explizit machen, bei POST ist Caching implizit abgeschaltet. Nutzt du Post-Redirect-Get (unterbindet die störende "möchten sie die Daten noch einmal senden" Meldung und verhindert unbeabsichtigte Mehrfacheingaben.), dann gilt das aber natürlich nicht für den GET und du musst dich mit Caching beschäftigen. Das ist einer der Fälle in denen Caching dann gerne ganz abgeschaltet wird, da die Ausgabe auch von der Session abhängt und nicht nur von der URL und den Daten.
Das Cachingverhalten ist im übrigen ein Indiz für die Idee der unterschiedlichen HTTP Methoden: Ein POST ist dafür gedacht, dass der Benutzer die Webressource ändert und man daher mit dem gecachten Ergebnis von vor der Änderung nichts anfangen kann. Ändert eine Listenausgabe die Daten? Nein, also ist GET hier angemessen.
Mit Sicherheit haben HTTP Methoden im übrigen nichts zu tun. Ein Angreifer manipuliert immer die ganze Anfrage an den Server, dem ist es egal, ob die Parameter in den Headern oder im Content stehen.
Willst du deine Addresszeile "sauber" halten (und damit Lesezeichen unterbinden), würde ich eher darüber nachdenken den Aufruf innerhalb der Seite über AJAX zu machen. Hat auch den optischen Vorteil, dass das Layout nicht neu geladen wird. Im Grunde ist das der Ansatz, denn ich als "heutigen Standard" sehe. Mit der Entwicklung guter JavaScript Frameworks in den letzten Jahren wandert die View vollständig in den Client, der Server stellt nur noch eine API zur Verfügung. Statt einer Anwendung, deren Ausgabe die HTML-View ist, hat man defacto zwei Anwendungen: Eine in JS und eine serverseitige. Neben der UX hat das auch Vorteile für die Entwicklung: Es forciert eine Trennung der Responsibilities, die gerade in der Webentwicklung viel zu gerne missachtet wird, obwohl sie eine Best Practice ist. Das Aussehen der Seite kann stark verändert werden, ohne die serverseitige Logik anfassen zu müssen, diese wird nur noch angepasst um neue Features zu implementieren oder Bugs zu fixen. Auch kann man die serverseitge Anwendung komplett ersetzen (zum Beispiel von PHP zu Java), ohne den Client ändern zu müssen. Das hat auch für die Qualitätssicherung Vorteile, weil man beide Anwendungen unabhängig testen kann. So kann ich zum Beispiel testen, ob der Client auf einen 500er korrekt reagiert, ohne einen Serverfehler provozieren zu müssen: Ich mocke einfach den Server und kann die Ausgabe beliebig festlegen.
So ist eine dicke Wall of Text geworden
Dieser Beitrag ist möglicherweise länger wie der Quellcode um den es geht, aber vielleicht konnte ich deine Fragen beantworten, oder dir zumindest Hinweise geben, was du als nächstes Fragen solltest.