Andi: Input Sanitization tatsächlich nicht notwendig? :O

Okay, vielleicht ein BISSCHEN provokant, aber hear me out:

Ist es tatsächlich sinnlos, User Input zu säubern?

"Angefangen" hat alles mit der Überlegung, was wohl passieren würde, wenn ich ein Passwort, das "&<>", etc. enthält, durch htmlspecialchars, filter_var, etc. jage, da dieses ja dann mit &lt; etc. verfälscht würde.

...was mich zu meiner eigentlichen (fatalen?) Erkenntnis bringt, dass ich meinen Input eigentlich GAR NICHT "desinfizieren" (sanitizen) muss! Nochmal, hear me out:

Gegeben: $_POST["username"] und $_POST["passwort"]

...solange ich diese nicht z.B. in einer Datenbank abspeichere, sollten sie mir doch nicht gefährlich werden können, oder? ...und selbst DA verwende ich Prepared Statements! (abgesehen davon, dass die Speicherung von Klartext-Passwörtern sowieso grob fahrlässig ist und daher selbige natürlich gehashed werden - ihre Ursprungserscheinung daher ändern...)

Ich könnte natürlich vielleicht den TYP von $_POST (String, Integer, Array, Binary, ...) bestimmen - aber selbst der ist in seiner ursprünglichen Form durch das HTTP Protokoll stets ein String, oder?

Wie soll da z.B. Cross-Site-Scripting möglich sein?

Natürlich gibt es auch FILTER_VALIDATE_EMAIL and the like, ....aber diese sagen nichts über die SICHERHEIT, sondern vielmehr über die WOHLGEFORMTHEIT des Inputs aus...?

Daher - eventuell trim() über den Input und gut ist?

Vielen Dank für eure Gedanken,

Andi

  1. Gegeben: $_POST["username"] und $_POST["passwort"]

    ...solange ich diese nicht z.B. in einer Datenbank abspeichere,

    Nun, das Passwort wirst Du ja hoffentlich durch die Password-Funktionen jagen. Aber der Benutzername? Der steht häufig mit dem Hash des Passworts in der Datenbank…

    Aus

    SELECT `pw_hash` FROM `shadow` WHERE username='$_POST["username"]';
    

    kann bei der Ersetzung ganz fix ...

    SELECT `pw_hash` FROM `shadow` WHERE username='';UPDATE shadow SET `pw_hash`='$2y$10$V2yZerKdNLhJiExdhBsQyuD3CcQwiUqelw6EnPlmj4oYM5OMBJoHC';
    

    ... werden.

    Dazu muss nur

    ';UPDATE shadow SET `pw_hash`='$2y$10$V2yZerKdNLhJiExdhBsQyuD3CcQwiUqelw6EnPlmj4oYM5OMBJoHC
    

    als „Passwort“ übermittelt werden - und schon hat jeder Nutzer "hallo" als Passwort". (Bevor jemand damit kommt: Konkret ist nochwas nötig…)

    "Angefangen" hat alles mit der Überlegung, was wohl passieren würde, wenn ich ein Passwort, das "&<>", etc. enthält, durch htmlspecialchars, filter_var, etc. jage, da dieses ja dann mit &lt; etc. verfälscht würde.

    Du willst das bitte richtig filtern. Es kommt auf den Kontext an, in welchen gewechselt wird!

  2. Hallo Andi,

    ...aber nicht so, wie Du meinst.

    Sanitizing eines Usernamens dürfte beinhalten:

    • Entfernen von führenden und abschließenden Spaces

    Mehr nicht.

    Wenn man manche Zeichen nicht zulassen möchte, kann man darauf prüfen und den Usernamen zurückweisen, aber das ist dann kein Sanitizing mehr, sondern Plausibilisierung.

    Sanitizing eines Passworts würde ich beim Login ebenfalls mit einem trim() durchführen und beim Vergeben des Passworts würde ich führende, abschließende und Spaces im Passwort zurückweisen. Eine Sequenz aus zwei Spaces auch. Denn solche Passwörter sind aus meiner Sicht ein Garant für "Ich komm nicht mehr rein"… Aber das ist deine Entscheidung.

    Mehr nicht.

    Für die DB Abfrage wird das Passwort gehasht und der Username entweder escaped oder, so wie bei Dir, als Argument an ein prepared statement gehängt.

    Mehr nicht.

    Wenn Du den Usernamen dann auf deiner Seite anzeigst, DANN musst Du ihn mit htmlspecialchars maskieren. Nicht sanitizen.

    Sanitizing machst Du, wenn klar ist, dass bestimmte Zeichen bei der Verarbeitung der Eingabe stören, wie z.B. Klammern und Minuszeichen in einer Telefonnummer. Die sanitized Du aber nur für die Nummernsuche in der DB, nicht generell.

    Sanitizing ist immer potenzieller Datenverlust. Daher muss man damit sehr zurückhaltend sein und gut überlegen, wo es sinnvoll ist. Aber dort ist es dann auch nötig.

    Rolf

    --
    sumpsi - posui - obstruxi
    1. und der Username entweder escaped oder, so wie bei Dir, als Argument an ein prepared statement gehängt.

      Mehr nicht.

      Dagegen spricht, dass es verbreitete Regeln für Benutzernamen gibt (kleine lateinische Buchstaben, Minus-Symbol und Ziffern, beginnt mit Buchstabe). Spätestens bei einer (womöglich später genutzten) Fremdauthentifizierung (also nicht durch die eigenen Skripte) können beliebige Benutzernamen (Genauer: Login-Namen) also patzen. Man müsste dann „mappen“. Das bedeutet: Aufwand, Fehlerquelle, …

      Nach einer Eingabe zwecks Login sollte man ihn also nur trimmen (wg. Copy & Paste - Fehlern), aber beim Anlegen strikter vorgehen: Aber nicht „sanieren“ sondern prüfen und ablehnen und die Eingabe ins HTML-Formular nicht zulassen.

    2. Hallo, danke euch für eure Antwort!

      …zusammenfassend ist also festzuhalten:

      1. für Passwörter genügt trim() da es sowieso gehashed wird?

      2. …bei usernames beschleicht mich allerdings tatsächlich ein schlechtes Gefühl, würde ich sie as is (analog zu den Passwörtern) abspeichern, da ich dann ja sozusagen Bomben in meiner Datenbank liegen hätte, die bei vielleicht zukünftigen Queries ohne escape/prepared Statements jederzeit hochgehen könnten 😪

      Daher meine Strategie:

      1. Passwörter nur trimmen und gehashed abspeichern
      2. Für Usernames:
          $username = trim($username);
          $username = htmlspecialchars($username, ENT_QUOTES, 'UTF-8');
      

      …und dann BEIDES via Prepared Statements einfügen.

      Schnapsidee oder bin ich endlich tatsächlich auf den heiligen Gral gestoßen?

      Dank euch, Andi

      1. Hallo, danke euch für eure Antwort!

        …zusammenfassend ist also festzuhalten:

        1. für Passwörter genügt trim() da es sowieso gehashed wird?

        2. …bei usernames beschleicht mich allerdings tatsächlich ein schlechtes Gefühl, würde ich sie as is (analog zu den Passwörtern) abspeichern, da ich dann ja sozusagen Bomben in meiner Datenbank liegen hätte, die bei vielleicht zukünftigen Queries ohne escape/prepared Statements jederzeit hochgehen könnten 😪

        Nein! Das ist NICHT richtig.

        Alle Date werden bei JEDEM Kontextwechsel entsprechend des neuen Kontextes behandelt.

        • Bei DB-Queries ist zu escapen,
        • bei Ausgaben ins HTML ist htmlspecialchars richtig.

        Und das jedesmal wieder.

        1. Alle Date werden bei JEDEM Kontextwechsel entsprechend des neuen Kontextes behandelt.

          • Bei DB-Queries ist zu escapen,
          • bei Ausgaben ins HTML ist htmlspecialchars richtig.

          Und das jedesmal wieder.

          Moment - alle Daten escapen mit Ausnahmen von Passwörtern, weil die sonst verfälscht würden und sowieso durch einen Hash Algorithmus laufen?

          Sorry für die Verwirrung 😅

          1. Alle Date werden bei JEDEM Kontextwechsel entsprechend des neuen Kontextes behandelt.

            • Bei DB-Queries ist zu escapen,
            • bei Ausgaben ins HTML ist htmlspecialchars richtig.

            Und das jedesmal wieder.

            Moment - alle Daten escapen mit Ausnahmen von Passwörtern, weil die sonst verfälscht würden und sowieso durch einen Hash Algorithmus laufen?

            Sorry für die Verwirrung 😅

            Sobald Du auf die auch sonst dumme Idee kommst, etwas wie

            <p>Das eingegebene Passwort „<?=$POST['password'];?>“ war leider falsch.</p>
            

            auszugeben muss natürlich das Passwort entschärft werden. Sonst faxt der Anwalt des Konkurrenten mit Kundenkonto, dessen Passwort „<canvas style="display:hidden">“ lautet, den Support an und beschwert sich „wegen des Fehlens von Impressum, Datenschutzerklärung und Nennung des Jugendschutzbeauftragten“ und schickt die Rechnung über 1500 Euro gleich mit…

            Ansonsten verbietet sich (fast) jede Änderung des eingegebenen Passwortes. Die Funktionen zur Erzeugung des Passwort-Hashes können (hoffentlich) sogar „Binärmüll“ (nicht darstellbare Zeichen) sicher verarbeiten und es gibt keinen einzigen vernünftigen oder einer kritischen Betrachtung standhaltenden Grund, ein für eine Anmeldung benutztes Passwort auch nur irgendwo wieder auszugeben oder anderweitig zu verwenden.

        2. …zusammenfassend ist also festzuhalten:

          1. für Passwörter genügt trim() da es sowieso gehashed wird?

          2. …bei usernames beschleicht mich allerdings tatsächlich ein schlechtes Gefühl, würde ich sie as is (analog zu den Passwörtern) abspeichern, da ich dann ja sozusagen Bomben in meiner Datenbank liegen hätte, die bei vielleicht zukünftigen Queries ohne escape/prepared Statements jederzeit hochgehen könnten 😪

          Nein! Das ist NICHT richtig.

          Alle Date werden bei JEDEM Kontextwechsel entsprechend des neuen Kontextes behandelt.

          • Bei DB-Queries ist zu escapen,
          • bei Ausgaben ins HTML ist htmlspecialchars richtig.

          Und das jedesmal wieder.

          Ausnahmen sind allenfalls erwartete Zahlenwerte, die entweder mit Funktionen wie is_numeric() als Zahlen bewiesen sind oder bei denen die Eigenschaft „ist Zahl“ vermittels boolval(), intval() oder floatval() als solche erzwungen wurde.

          Immer wenn Werte aus Quellen stammen, die man nicht zu 100% unter eigener Kontrolle hat, ist zu prüfen und ggf. zu entsprechend des neuen Kontextes escapen.

          Beispiel für „nicht zu 100% unter eigener Kontrolle“:

          Es wird eine Adressverwaltung „HorstsSuperAddr" (Name gewillkürt) als Libary eingebunden. Die Daten stammen von Drittquellen (Userinput ist nur eine von vielen Möglichkeiten). Bisher war in der Datenbank das Feld für Hausnummer eine Zahl. Resultierende Annahme: „Die Datenbank liefert Hausnummer als ganze Zahlen denn sie kann als Hausnummer nur solche speichern.“

          … Bis zum Update auf Version 08.15:

          Nach Hinweisen auf Probleme beim Import wird von den Autorer die Libary „HorstsSuperAddr" geändert. Das Datenbankfeld ist jetzt vom Typ Text, damit nicht nur die Hausnummer 6 sondern auch „6A“ gespeichert werden kann.

          Folge: Die Werte aus der Datenbank müssen ab sofort wie Text behandelt werden, bei Ausgaben Richtung Browser also durch htmlspecialchars() laufen.

          Sonst kann jemand nach Übermittlung der angeblichen Hausnummer

          7 <script>location.href="https://downloads.kleinweich.ru";</script>`
          

          seinen Marketingerfolg feiern, denn dort gibt's keine vegane Kuscheltiere - was vermuten lässt, das sodann „längst nicht alle Benutzer der Webseite erfreut sein werden“.

  3. Lieber Andi,

    Daher - eventuell trim() über den Input und gut ist?

    das Wichtigste wurde ja alles schon erwähnt. Meiner Meinung nach sollten Benutzernamen aber grundsätzlich case-insensitive sein. UserA sollte meiner Meinung nach identisch zu uSERa und usera behandelt werden. Insbesondere bei Mailadressen ist es sehr sinnvoll sie so zu behandeln. Wer bei einem Mailprovider Kunde ist, der BoB@example.org und bOb@example.org als unterschiedliche Mailkonten führt, hat dann zwar Pech, die Schuld schöbe ich aber dem Betreiber des Mailservers in die Schuhe, weil es echt Unsinn ist!

    Liebe Grüße

    Felix Riesterer

    1. @@Felix Riesterer

      Wer bei einem Mailprovider Kunde ist, der BoB@example.org und bOb@example.org als unterschiedliche Mailkonten führt, hat dann zwar Pech, die Schuld schöbe ich aber dem Betreiber des Mailservers in die Schuhe, weil es echt Unsinn ist!

      Und IIRC ein Verstoß gegen die Spec. AFAIR sind Mailadressen grundsätzlich case-insensitiv zu behandeln.

      🖖 Живіть довго і процвітайте

      --
      Ad astra per aspera
      1. Hallo,

        Wer bei einem Mailprovider Kunde ist, der BoB@example.org und bOb@example.org als unterschiedliche Mailkonten führt, hat dann zwar Pech, die Schuld schöbe ich aber dem Betreiber des Mailservers in die Schuhe, weil es echt Unsinn ist!

        Und IIRC ein Verstoß gegen die Spec. AFAIR sind Mailadressen grundsätzlich case-insensitiv zu behandeln.

        das hatte ich auch so im Hinterkopf - aber ich habe keine Stelle in den einschlägigen RFCs gefunden, die das stützt.

        Einen schönen Tag noch
         Martin

        --
        Wo wir sind, ist das Chaos. Aber wir können leider nicht überall sein.
        1. @@Der Martin

          Wer bei einem Mailprovider Kunde ist, der BoB@example.org und bOb@example.org als unterschiedliche Mailkonten führt, hat dann zwar Pech, die Schuld schöbe ich aber dem Betreiber des Mailservers in die Schuhe, weil es echt Unsinn ist!

          Und IIRC ein Verstoß gegen die Spec. AFAIR sind Mailadressen grundsätzlich case-insensitiv zu behandeln.

          das hatte ich auch so im Hinterkopf - aber ich habe keine Stelle in den einschlägigen RFCs gefunden, die das stützt.

          Kannst du auch nicht, weil das nicht stimmt?

          “Verbs and argument values […] are not case sensitive, with the sole exception in this specification of a mailbox local-part […] The local-part of a mailbox MUST BE treated as case sensitive.” [RFC 5321 §2.4]

          I stand corrected.

          example.org sollte also gleich behandelt werden wie EXAMPLE.ORG und eXaMpLe.OrG; aber BoB und bOb können verschiedene Adressen sein.

          Allerdings heißt es weiter im Text: “However, exploiting the case sensitivity of mailbox local-parts impedes interoperability and is discouraged. Mailbox domains follow normal DNS rules and are hence not case sensitive.”

          🖖 Живіть довго і процвітайте

          --
          Ad astra per aspera
  4. problematische Seite

    Hallo,

    **selbstverständlich muss User-Input immer kontrolliert werden! ** Allerdings hängt das erforderliche Maßnahmenbündel immer vom Zielkontext der Daten ab!

    Es gibt dazu einen Artikel, ursprünglich von Dedlfix, der schon die meisten Probleme aufzeigt.

    Bezüglich Verwendung von User-Inputs als Dateinamen sollten zusätziche Überlegungen angestellt werden.

    Einige davon kann man im Artikel zum Fileupload nachlesen. Den fand ich leider nur noch nach einiger Suche über Google wieder!(?)

    Deine Frage ist also tasächlich provokativ und zeugt von wenig Recherchebemühungen im SelfHTML-Raum und in den Suchmaschinen :-(

    Sollte deine Frage darauf gerichtet sein, ob die Sanitize-Funktionen von PHP sinnvoll sind, so mag ich nur antworten: Verlässliche Polymorphie ist hier nicht erkennbar; bau die notwendigen Kontextwechsel und Filter also lieber selber, denn sie sind immer vom ZIEL abhängig, und nicht von der Quelle!

    Grüße Helmut