Malcolm Beck´s: Frage zu Prepared Statement

hi,

ich hab nun endlich diese Prepared Statements Geschichte verstanden, also, ich krieg statt errormeldungen nun das Gewünschte Resultat.

Aber ich bin noch etwas verunsichert, insbesondere wegen SQL-Injection, daher die Frage:

Wie übergebe ich $_SERVER arrays und Usereingaben an das verarbeitende Script?

Es geht um folgendes Schnippsel:

  
$myquery = $_SERVER['REQUEST_URI'];   // muss ich hier Escapen?  
$myquery = $_GET['kategorie'];        // muss ich hier Escapen?  
  
$stmt =  $verbindung->stmt_init();  
  
if ($stmt->prepare("  
SELECT  
 mein_menu.haupt_gruppe  
FROM  
 mein_menu  
WHERE  
 mein_menu.haupt_gruppe = ?  
")) {  
// warum bin ich der einzige, der mit . seine Tabellen verknüpfen muss?  
  
    $stmt->bind_param("s", $myquery);  
  
    $stmt->execute();  
  
    $stmt->bind_result($haupt_gruppe);  
  
    $stmt->fetch();  
  
    printf("%s Title %s\n", htmlspecialchars($myquery), $haupt_gruppe);  
  
// wie baue ich aus dem Resultat ein array?  
  
    $stmt->close();  
}

mfg

  1. Moin Moin!

    ich hab nun endlich diese Prepared Statements Geschichte verstanden, also, ich krieg statt errormeldungen nun das Gewünschte Resultat.

    Aber ich bin noch etwas verunsichert, insbesondere wegen SQL-Injection, daher die Frage:

    Wie übergebe ich $_SERVER arrays und Usereingaben an das verarbeitende Script?

    Es geht um folgendes Schnippsel:

    [code lang=php]
    $myquery = $_SERVER['REQUEST_URI'];   // muss ich hier Escapen?

    Nein, denn die nächste Zeile überschreibt die Variable sofort wieder.

    $myquery = $_GET['kategorie'];        // muss ich hier Escapen?

    Nein, verifizieren: #1039

    Wenn der Kategorieparameter ein positivier Integer sein soll, dann prüfe das z.B. so (Perl-Syntax): $myquery=~/^\d+$/ or die "Hack mich nicht!";

    $stmt =  $verbindung->stmt_init();

    if ($stmt->prepare("
    SELECT
    mein_menu.haupt_gruppe
    FROM
    mein_menu
    WHERE
    mein_menu.haupt_gruppe = ?
    ")) {
    // warum bin ich der einzige, der mit . seine Tabellen verknüpfen muss?

    Mußt Du  ja nicht. Das ist in diesem Stückchen Code vollkommen überflüssig, es sei denn, Du machst sehr exotische Sachen mit der Datenbank.

    $stmt->bind_param("s", $myquery);

    $stmt->execute();

    $stmt->bind_result($haupt_gruppe);

    $stmt->fetch();

    Boah, ist das umständlich. Kann PHP das nicht eleganter? Perl's DBI kommt ohne bind_result aus, stattdessen liefert eine von mehreren fetch-Funktionen direkt ein Array, eine Array-Referenz oder eine Hash-Referenz zurück. Das bind()en von Ausgabeparametern geht natürlich auch, macht aber mehr Tipparbeit.

    printf("%s Title %s\n", htmlspecialchars($myquery), $haupt_gruppe);

    // wie baue ich aus dem Resultat ein array?

    Wiederhole fetch, bis es kein Ergebnis mehr liefert. Bei jedem Durchlauf wird $haupt_gruppe mit der nächsten Ergebniszeile überschrieben. Ich denke, fetch() dürfte einen entsprechenden Rückgabewert haben => RT(F)M.

    $stmt->close();

    Erst nachdem fetch nichts mehr liefert oder dich der Rest der Ergebnisse nicht mehr interessiert.

    Alexander

    --
    Today I will gladly share my knowledge and experience, for there are no sweeter words than "I told you so".
    1. echo $begrüßung;

      $stmt->execute();
          $stmt->bind_result($haupt_gruppe);
          $stmt->fetch();

      Boah, ist das umständlich. Kann PHP das nicht eleganter?

      PHP setzt hier nur 1:1 die MySQL-API um. Einfacher ist es in PDO gelöst. Da wurden aber nach meinem schon eine geraume Weile zurückliegendem Test Prepared Statements für MySQL nur simuliert.

      printf("%s Title %s\n", htmlspecialchars($myquery), $haupt_gruppe);

      $haupt_gruppe wurde im Gegensatz zu $myquery nicht kontextgerecht behandelt.

      // wie baue ich aus dem Resultat ein array?

      Umständlich oder gleich mit PDO

      echo "$verabschiedung $name";

      1. hi,

        printf("%s Title %s\n", htmlspecialchars($myquery), $haupt_gruppe);
        $haupt_gruppe wurde im Gegensatz zu $myquery nicht kontextgerecht behandelt.

        $haupt_gruppe kommt direkt aus der DB, den Inhalt von $haupt_gruppe habe ich geschrieben, muss ich dass auch escapen?

        Umständlich oder gleich mit PDO

        PDO klingt interessant, muss ich, um diese zu nutzen, einen datenbankspezifischen PDO-Treiber benutzen, ich will ja wenn, nur ein zwei features verwenden.
        Und wenn ja, wie kann ich prüfen, ob PDO-Treiber bei mir (1und1) installiert sind?

        mfg

        1. Hallo Malcolm,

          printf("%s Title %s\n", htmlspecialchars($myquery), $haupt_gruppe);
          $haupt_gruppe wurde im Gegensatz zu $myquery nicht kontextgerecht behandelt.

          $haupt_gruppe kommt direkt aus der DB, den Inhalt von $haupt_gruppe habe ich geschrieben, muss ich dass auch escapen?

          ja.

          Du hattest doch gelernt: Behandle immer kontextgerecht. Das ist viel einfacher und weniger fehleranfällig als ständig zu überlegen, ob man die kontextgerechte Behandlung in diesem gerade vorliegenden Spezialfall weglassen könnte.

          Wie Du Datenbankobjekte von MySQL kontextgerecht behandelst, findest Du im Handbuchabschnitt Schema Object Names.

          Freundliche Grüße

          Vinzenz

          1. hi Vinzenz,

            $haupt_gruppe kommt direkt aus der DB, den Inhalt von $haupt_gruppe habe ich geschrieben, muss ich dass auch escapen?
            Du hattest doch gelernt: Behandle immer kontextgerecht. Das ist viel einfacher und weniger fehleranfällig als ständig zu überlegen, ob man die kontextgerechte Behandlung in diesem gerade vorliegenden Spezialfall weglassen könnte.

            Damit werde ich aber Schwierigkeiten bekommen, in meiner Datenbank stehen die Inhalte der Seite so, wie sie im HTML ausgegeben werden, beispielsweise <p>Mein Text</p> oder auch <img src="/bild.jpg" alt="" />, hier kann ich htmlspecialchars() nicht anwenden.

            Das ist auch eines der Punkte, die ich überhaupt nicht verstehe, wie schreibt ihr eure Daten in die DB?
            Im Falle von <p></p> wäre es ja einfach, da könnte ich eine Regexp schreiben,

            ------------Textarea-----
            \n
            \n
            Mein Text, Infos und so weiter.
            \n
            \n
            -------------------------

            Hier könnte ich alles zwischen \n\n(.*)\n\n in "<p>$1</p>" packen, nur, spätestens wenn ich ein Bild in den Text einbinden möchte, muss ich wieder HTML schreiben.

            Wie macht ihr das?

            Wie Du Datenbankobjekte von MySQL kontextgerecht behandelst, findest Du im Handbuchabschnitt Schema Object Names.

            Danke für den Link.

            mfg

            1. echo $begrüßung;

              $haupt_gruppe kommt direkt aus der DB, den Inhalt von $haupt_gruppe habe ich geschrieben, muss ich dass auch escapen?
              Du hattest doch gelernt: Behandle immer kontextgerecht. Das ist viel einfacher und weniger fehleranfällig als ständig zu überlegen, ob man die kontextgerechte Behandlung in diesem gerade vorliegenden Spezialfall weglassen könnte.
              Damit werde ich aber Schwierigkeiten bekommen, in meiner Datenbank stehen die Inhalte der Seite so, wie sie im HTML ausgegeben werden, beispielsweise <p>Mein Text</p> oder auch <img src="/bild.jpg" alt="" />, hier kann ich htmlspecialchars() nicht anwenden.

              Woher weiß man das denn? Wenn das ein Feature und keine Sicherheitslücke ist, dann kommentiere das in deinem Code, so dass das geschulte Auge nicht darüber stolpert.

              echo "$verabschiedung $name";

              1. hi,

                Damit werde ich aber Schwierigkeiten bekommen, in meiner Datenbank stehen die Inhalte der Seite so, wie sie im HTML ausgegeben werden, beispielsweise <p>Mein Text</p> oder auch <img src="/bild.jpg" alt="" />, hier kann ich htmlspecialchars() nicht anwenden.

                Woher weiß man das denn? Wenn das ein Feature und keine Sicherheitslücke ist, dann kommentiere das in deinem Code, so dass das geschulte Auge nicht darüber stolpert.

                Wie meinst du das mit kommentieren, für hier?

                Ich ging davon aus, dass ich Daten, die aus meiner DB kommen nicht mehr behandeln brauche, diese sind ja bereits in der DB, bevor sie in die DB kamen, habe ich sie ja bereits behandelt.

                mfg

                1. echo $begrüßung;

                  Ich ging davon aus, dass ich Daten, die aus meiner DB kommen nicht mehr behandeln brauche, diese sind ja bereits in der DB, bevor sie in die DB kamen, habe ich sie ja bereits behandelt.

                  Das vorsorgliche Behandeln für irgendeinen Kontext, der später mal zum Tragen kommt ist im Allgemeinen eine schlechte Idee. Wenn dein Ziel aber ein CMS ist, dass fix- und fertigen HTML-Code speichert, dann ist bei der Ausgabe keine Behandlung mehr vorzunehmen. Er wäre nur gut, wenn du das an der entsprechenden Ausgabestelle per Kommentar dokumentierst, so dass du und andere später wissen, dass hier bereits alles in Butter ist.

                  echo "$verabschiedung $name";

                  1. hi,

                    bevor sie in die DB kamen, habe ich sie ja bereits behandelt.
                    Das vorsorgliche Behandeln für irgendeinen Kontext, der später mal zum Tragen kommt ist im Allgemeinen eine schlechte Idee.

                    Damit meinte ich mysqli_real_escape_string(), wobei ich mich immer noch Frage, warum ich mich vor mir selber schützen muss  :)
                    Aber passt schon, schaden tut es ja nicht.

                    Wenn dein Ziel aber ein CMS ist, dass fix- und fertigen HTML-Code speichert, dann ist bei der Ausgabe keine Behandlung mehr vorzunehmen. Er wäre nur gut, wenn du das an der entsprechenden Ausgabestelle per Kommentar dokumentierst, so dass du und andere später wissen, dass hier bereits alles in Butter ist.

                    Ja, dass muss ich mir auch angewöhnen, die Kommentare in meinen Scripten sind  kürzer als meine Problembeschreibungen hier.

                    Wie ist denn die vorgehensweise, HTML-Code fix und fertig in der DB zu speichern?
                    Wird das in gängigen CMS so gehandhabt?

                    mfg

                    1. Moin Moin!

                      Wie ist denn die vorgehensweise, HTML-Code fix und fertig in der DB zu speichern?
                      Wird das in gängigen CMS so gehandhabt?

                      Darauf würde ich nicht wetten, denn damit legt man sich auf HTML als einziges Ausgabemedium fest. Viel eleganter ist es, jedes einzelne Seitenelement "roh" zu speichern und die Umwandlung in das gewünschte Format (z.B. HTML, PDF, Excel, Word, LaTeX) erst bei der Ausgabe zu erledigen, typischerweise mit Templates. Braucht man dann ein neues Format, z.B. OpenOffice, schreibt man "nur" ein neuen Satz Templates und schon sind alle Inhalte auch im OpenOffice-Format verfügbar.

                      Alexander

                      --
                      Today I will gladly share my knowledge and experience, for there are no sweeter words than "I told you so".
                      1. hi,

                        Wird das in gängigen CMS so gehandhabt?
                        Darauf würde ich nicht wetten, denn damit legt man sich auf HTML als einziges Ausgabemedium fest. Viel eleganter ist es, jedes einzelne Seitenelement "roh" zu speichern und die Umwandlung in das gewünschte Format (z.B. HTML, PDF, Excel, Word, LaTeX) erst bei der Ausgabe zu erledigen, typischerweise mit Templates.

                        Das wollte ich Anfangs noch so machen, dann aber stand ich vor dem Problem, Bilder, Links oder auch Videos in den Inhalt einzufügen.
                        Wenigstens konnte ich mein RSS-Feed Dynamisch bauen, die Inhalte für dieses kommen auch aus der DB.

                        Braucht man dann ein neues Format, z.B. OpenOffice, schreibt man "nur" ein neuen Satz Templates und schon sind alle Inhalte auch im OpenOffice-Format verfügbar.

                        Wäre das nicht auch mit ein Paar Regexp machbar, die Inhalte einmal kurz durch den Parser jagen, alle unerwünschten HTML-Codes entfernen und dann erst an das gewünschte Template übergeben?

                        mfg

                        1. Moin Moin!

                          Das wollte ich Anfangs noch so machen, dann aber stand ich vor dem Problem, Bilder, Links oder auch Videos in den Inhalt einzufügen.

                          Wo ist das Problem? Entweder speicherst Du in der DB einen Dateinamen, oder Du packst den Inhalt in ein BLOB.

                          Du kannst natürlich nicht den gesamten Seiteninhalt in ein Stückchen Text packen. Die Seite besteht dann z.B. aus Überschrift 17, Absatz 43, Absatz 45, Bild 93, Absatz 47. In HTML wird daraus <h1>..</h1><p>..</p><p>..</p><img ..><p>..</p>. Wie das als Word aussieht, mag ich mir gar nicht vorstellen. ;-)

                          Wenigstens konnte ich mein RSS-Feed Dynamisch bauen, die Inhalte für dieses kommen auch aus der DB.

                          Wäre das nicht auch mit ein Paar Regexp machbar, die Inhalte einmal kurz durch den Parser jagen, alle unerwünschten HTML-Codes entfernen und dann erst an das gewünschte Template übergeben?

                          Nö. Um HTML komplett zu verdauen, reichen REs nicht aus. Du kannst natürlich ein HTML-Subset nehmen und einen gemischten Ansatz fahren, um innerhalb des Absatzes kleinere Formatierungen zu erlauben. Oder Du baust einen Absatz aus mehreren kleineren Elementen, die einzeln in der DB liegen. Damit wären z.B. auch Bilder innerhalb der Absätze möglich.

                          Alexander

                          --
                          Today I will gladly share my knowledge and experience, for there are no sweeter words than "I told you so".
                    2. echo $begrüßung;

                      Das vorsorgliche Behandeln für irgendeinen Kontext, der später mal zum Tragen kommt ist im Allgemeinen eine schlechte Idee.
                      Damit meinte ich mysqli_real_escape_string(), wobei ich mich immer noch Frage, warum ich mich vor mir selber schützen muss  :)

                      Das Behandeln der Daten für das SQL-Statement ist nur für dieses SQL-Statement da, damit einige Zeichen in den Daten von den sie umgebenden Begrenzungszeichen unterschieden werden können. Der Grund der Behandlung ist also nicht hauptsächlich der Schutz vor SQL-Injection. Das ist nur ein Nebeneffekt. Du musst dich insofern vor die selbst schützen, als du mal auf die Idee kommst, 'Malcolm Beck's' einfügen zu wollen. Dann hast du den Salat, wenn du das nicht als 'Malcolm Beck's' übergibst.

                      Wie ist denn die vorgehensweise, HTML-Code fix und fertig in der DB zu speichern?
                      Wird das in gängigen CMS so gehandhabt?

                      "Einfach rein mit dem HTML" ist eine einfache Vorgehensweise. Doch die hat gavierende Nachteile, wenn es darum geht, den Inhalt in einem anderem Format ausgeben zu wollen. Oder einfach mal Inhalt suchen, ohne dass man Code-Teile findet, ist so gut wie unmöglich. Allerdings hast du das letztere Problem auch dann, wenn du Format-Information ablegen willst. Wenn mitten im Fließtext ein Link stehen soll, so sind mindestens irgendwelche Zeichen enthalten, die die Stelle kennzeichnen. Auch wenn man die Meta-Informationen anderweitig ablegt, bleibt was im Text zurück. Man könnte dieses Problem vielleicht so lösen, dass der Text in Reinform abgelegt wird und an anderer Stelle Position, Länge und Art der Formatierung hinterlegt werden. Dann muss man aber beides beim Ausgeben wieder zusammenfummeln.

                      echo "$verabschiedung $name";

                      1. hi,

                        Das Behandeln der Daten für das SQL-Statement ist nur für dieses SQL-Statement da, damit einige Zeichen in den Daten von den sie umgebenden Begrenzungszeichen unterschieden werden können ... Du musst dich insofern vor die selbst schützen, als du mal auf die Idee kommst, 'Malcolm Beck's' einfügen zu wollen. Dann hast du den Salat, wenn du das nicht als 'Malcolm Beck's' übergibst.

                        Genau das musste mir mal gesagt werden, jetzt verstehe ich auch den Sinn dahinter, Danke.

                        Wird das in gängigen CMS so gehandhabt?
                        "Einfach rein mit dem HTML" ist eine einfache Vorgehensweise. Doch die hat gavierende Nachteile, wenn es darum geht, den Inhalt in einem anderem Format ausgeben zu wollen.

                        Mein eigentliches Ziel bestand in erster Linie darin, mich von den ganzen Ordnern und Dateien, die ja für jede Seite der Webpräsenz gebraucht werden, zu trennen.
                        Zumal das Updaten der Seiten und neue Inhalte einfügen mit einer DB viel einfacher von der Hand gehen.

                        Oder einfach mal Inhalt suchen, ohne dass man Code-Teile findet, ist so gut wie unmöglich.

                        Das würde ich wahrscheinlich noch mit ein paar Regexp hinbekommen, aber

                        Allerdings hast du das letztere Problem auch dann, wenn du Format-Information ablegen willst. Wenn mitten im Fließtext ein Link stehen soll, so sind mindestens irgendwelche Zeichen enthalten, die die Stelle kennzeichnen. Auch wenn man die Meta-Informationen anderweitig ablegt, bleibt was im Text zurück. Man könnte dieses Problem vielleicht so lösen, dass der Text in Reinform abgelegt wird und an anderer Stelle Position, Länge und Art der Formatierung hinterlegt werden. Dann muss man aber beides beim Ausgeben wieder zusammenfummeln.

                        Das wäre mir ein wenig zu viel bzw. zu hoch, mit dem bisschen PHP Wissen (vllt. ein halbes/dreiviertel Jahr[1]) und den 2 Monaten jetzt mit MySQL wäre ich mit dieser Aufgabe Maßlos überfordert. Vielleicht später mal, wenn ich Programmieren kann.

                        Jedenfalls Danke für die Hilfe.

                        mfg
                        [1] davor zwar auch schon ein wenig rumprobiert, aber include('navi.html'); kann ich wohl nicht als Programmiertechnische Glanzleistung zählen ;)

    2. hi,

        
      
      > > $myquery = $_SERVER['REQUEST_URI'];   // muss ich hier Escapen?
      
      

      Nein, denn die nächste Zeile überschreibt die Variable sofort wieder.

      Ich wollte das Posting nicht zu gross gestalten, Generell meinte ich alle URI Abfragen, egal ob Query oder Request Uri

      $myquery = $_GET['kategorie'];        // muss ich hier Escapen?
      Nein, verifizieren: #1039

      Genau deshalb frage ich, es heisst:

      // Die Parameter für Prepared Statements müssen nicht maskiert werden. Der Treiber übernimmt das für Sie.

      Leider steht nirgends geschrieben, wie dass mit den Usereingaben, egal woher sie kommen, aussieht.
      Es ist ja auch nicht schwer die Eingaben zu validieren,

      $myquery = mysqli_real_escape_string($verbindung, $_GET['kategorie']);

      Nur weiss ich nicht, ob dass jetzt doppelt escaped wird, dass wäre ja nicht erwünscht.

      dann prüfe das z.B. so (Perl-Syntax): $myquery=~/^\d+$/ or die "Hack mich nicht!";

      Brauch ich ja nicht einmal, es geht nur um escapen.

      // warum bin ich der einzige, der mit . seine Tabellen verknüpfen muss?
      Mußt Du  ja nicht. Das ist in diesem Stückchen Code vollkommen überflüssig, es sei denn, Du machst sehr exotische Sachen mit der Datenbank.

      Ahh, das brauche ich wohl nur, wenn ich in der SELECT mehrere Tabellen anspreche, Danke.

      // wie baue ich aus dem Resultat ein array?
      Wiederhole fetch, bis es kein Ergebnis mehr liefert. Bei jedem Durchlauf wird $haupt_gruppe mit der nächsten Ergebniszeile überschrieben. Ich denke, fetch() dürfte einen entsprechenden Rückgabewert haben => RT(F)M.

      Ja, IRT(F)M, der Rückgabewert ist String oder Integer.

      Ich werd's mal mit der PDO probieren.

      mfg

      1. Moin Moin!

        Genau deshalb frage ich, es heisst:

        // Die Parameter für Prepared Statements müssen nicht maskiert werden. Der Treiber übernimmt das für Sie.

        Leider steht nirgends geschrieben, wie dass mit den Usereingaben, egal woher sie kommen, aussieht.

        Exakt so. Es sind Parameter für prepared Statements, egal woher sie kommen.

        Es ist ja auch nicht schwer die Eingaben zu validieren,

        $myquery = mysqli_real_escape_string($verbindung, $_GET['kategorie']);

        Nur weiss ich nicht, ob dass jetzt doppelt escaped wird, dass wäre ja nicht erwünscht.

        Wird es. Das Escapen übernimmt irgendwelcher C-Code hinter bind und execute, falls notwendig, oder idealerweise werden die Daten freundlicherweise getrennt vom SQL übertragen, so dass überhaupt kein Escaping notwendig ist, nicht einmal im C-Code.

        dann prüfe das z.B. so (Perl-Syntax): $myquery=~/^\d+$/ or die "Hack mich nicht!";

        Brauch ich ja nicht einmal, es geht nur um escapen.

        Du mußt JEDE EINZELNE Benutzereingabe validieren. Validieren ist nicht Escapen. Validieren bedeutet, die Benutzereingabe auf Typ, Größe, Wert, Konsistenz zu prüfen. Wenn Du das konsequent machst, und zwar so paranoid wie möglich, dann ist ein Angriff über manipulierte Benutzereingaben schon einmal extrem schwierig. Was passiert, wenn $myquery zwar auf das Pattern /^\d+/ paßt, aber die Zahl, sagen wir mal, 100 Stellen hat? Was macht Dein Code dann?

        Perls Taint-Mode ist in Sachen Eingabevalidierung extrem hilfreich, arbeitest Du mit unvalidierten Daten im Taint-Mode, fällst Du spätestens beim Zugriff auf Syscalls, Dateisystem oder Datenbank auf die Schnauze, sprich: das Programm wird beendet, bevor die unverifizierten Daten schaden anrichten können.

        Alexander

        --
        Today I will gladly share my knowledge and experience, for there are no sweeter words than "I told you so".
        1. hi,

          $myquery = mysqli_real_escape_string($verbindung, $_GET['kategorie']);
          Nur weiss ich nicht, ob dass jetzt doppelt escaped wird, dass wäre ja nicht erwünscht.

          Wird es. Das Escapen übernimmt irgendwelcher C-Code hinter bind und execute, falls notwendig,

          Also könnte ich, wenn ich mit Prepared Statements arbeite, auf mysqli_real_escape_string verzichten, ich werde das Risiko einfach mal eingehen :)

          oder idealerweise werden die Daten freundlicherweise getrennt vom SQL übertragen, so dass überhaupt kein Escaping notwendig ist, nicht einmal im C-Code.

          Wie soll das funktionieren?

          Brauch ich ja nicht einmal, es geht nur um escapen.

          Du mußt JEDE EINZELNE Benutzereingabe validieren. Validieren ist nicht Escapen. Validieren bedeutet, die Benutzereingabe auf Typ, Größe, Wert, Konsistenz zu prüfen. Wenn Du das konsequent machst, und zwar so paranoid wie möglich, dann ist ein Angriff über manipulierte Benutzereingaben schon einmal extrem schwierig.

          Ja, dass war jetzt auch für mich Verständlich, ich ging die ganze Zeit davon aus, "User eingaben validieren" wäre mit escapen erledigt.
          Logisch, und wenn man es so liest, auch Selbstverständlich.
          So kann ich ja auch direkt eingaben wie ?irgendwas=include('hackerzeugs'), was recht häufig in meinen Statistiken auftaucht, direkt mit die() beenden.

          Perls Taint-Mode ist in Sachen Eingabevalidierung extrem hilfreich,

          Perl kann ich nicht verwenden, also ich, mein Server schon, wäre mir aber auch zu kompliziert, jetzt noch Perl mit ins Spiel zu bringen.

          Danke für die Hilfe.

          mfg

          1. hi,

            Du mußt JEDE EINZELNE Benutzereingabe validieren. Validieren ist nicht Escapen. Validieren bedeutet, die Benutzereingabe auf Typ, Größe, Wert, Konsistenz zu prüfen. Wenn Du das konsequent machst, und zwar so paranoid wie möglich, dann ist ein Angriff über manipulierte Benutzereingaben schon einmal extrem schwierig.

            Das öffnet ja völlig neue Türen, ich habe mittlerweile auch einen Einstieg gefunden, jetzt fehlen mir die Ideen, was sollte Sinnvollerweise alles geprüft werden, bzw. auf was soll geprüft werden?

            Was ich bis jetzt habe (ich bin mir Sicher, es geht auch schöner):

            if($_GET) {  
             foreach($_GET as $keys => $vals) {  
              
             /* Wenn 'http' oder 'inc' in den $_GET-Variablen vorkommen, Fehler melden */  
              
                if ((preg_match_all("|http|", "$keys $vals", $mein_get)) OR (preg_match_all("|inc|", "$keys $vals", $mein_get)))  
                {  
                     echo 'Hab ich dich!';  
                }  
              
             /* Wenn $_GET Key oder Value mehr als 30 Zeichen hat, Fehler melden */  
              
                if ((strlen($keys) >= 30) OR (strlen($vals) >= 30))  
                {  
                     echo 'URI zu lang';  
                }  
              
             }  
            }
            

            Im Einsatz könnte ich dann statt Fehlermeldungen auszugeben, das Script beenden.

            Was wäre hier alles Sinnvoll und was sollte ich definitiv meiden?

            mfg

            1. hi,

              ist das jetzt eines der grossen Webentwickler Geheimnisse, die, wenn man es verstanden hat, für sich behält?  :)

              Naja, ich hab jedenfalls ein wenig weiter getestet, Aktueller Stand:
              http://start-navi.de/beispiele/?test=malcolm

              mfg

            2. Moin Moin!

              Du mußt JEDE EINZELNE Benutzereingabe validieren. Validieren ist nicht Escapen. Validieren bedeutet, die Benutzereingabe auf Typ, Größe, Wert, Konsistenz zu prüfen. Wenn Du das konsequent machst, und zwar so paranoid wie möglich, dann ist ein Angriff über manipulierte Benutzereingaben schon einmal extrem schwierig.

              Das öffnet ja völlig neue Türen, ich habe mittlerweile auch einen Einstieg gefunden, jetzt fehlen mir die Ideen, was sollte Sinnvollerweise alles geprüft werden, bzw. auf was soll geprüft werden?

              Was ich bis jetzt habe (ich bin mir Sicher, es geht auch schöner):

              [code lang=php]if($_GET) {
              foreach($_GET as $keys => $vals) {

              /* Wenn 'http' oder 'inc' in den $_GET-Variablen vorkommen, Fehler melden */

              if ((preg_match_all("|http|", "$keys $vals", $mein_get)) OR (preg_match_all("|inc|", "$keys $vals", $mein_get)))
                  {
                       echo 'Hab ich dich!';
                  }

              Öm, hier machst Du es genau falschrum. Suche nicht nach "bösem" und nimm den Rest als "gut". Du kennst nicht alles "böse", und wenn es einen neuen Angriffsweg gibt, mußt Du nachrüsten.

              Nimm stattdessen alles als "böse" an und beweise für jeden einzelnen Parameter, dass er gut ist. Und zwar für jeden nach individuellen Regeln. Wenn Du eine numerische ID und einen bis zu 255 Zeichen langen Text erwartest, prüfe den einen Parameter mindestens auf "nur Ziffern" plus Wertebereich und den anderen mindestens auf die Länge.

              Auf Dauer wirst Du Dir wohl die eine oder andere Validierungsroutine anlegen -- isValidCount(), isValidInteger(), isValidFloat(), isValidName(), isValidISBN(), isValidURL(), isValidEMail(), usw. (die letzen beiden nur eingeschränkt, Du kannst bestenfals das Format prüfen).

              /* Wenn $_GET Key oder Value mehr als 30 Zeichen hat, Fehler melden */

              if ((strlen($keys) >= 30) OR (strlen($vals) >= 30))
                  {
                       echo 'URI zu lang';
                  }

              Das ist irgendwie wirr. Warum erlaubst Du keine Parameter-Namen über 30 Zeichen? Warum keine Parameter-Werte über 30 Zeichen? Und was soll diese Prüfung überhaupt?

              Um noch einmal auf das Beispiel ID+Text zurückzukommen:

              http://www.example.com/hurtmeplenty.php?id=42;text=bla%20fasel

              http://www.example.com/hurtmeplenty.php?id=42;text=bla%20fasel;waaah-grosses-chaos-wird-geschehen-der-weltuntergang=ist-nahe-wir-werden-alle-sterben-und-nur-weil-dieser-parameter-komplett-ignoriert-wird

              Dich interessieren nur "id" und "text". Also ignoriere den Rest. Kein Grund, wegen überflüssiger Parameter Panik zu schieben. Weitergeben darfst Du den Müll natürlich auch nicht -- erst wenn der Parameter validiert ist.

              isValidInteger($_GET['id']) or die "Crackeralarm!";
              isValidString($_GET['text'],1,255) or die "Crackeralarm!";
              machWasMit($_GET['id'],$_GET['text']);

              Im Einsatz könnte ich dann statt Fehlermeldungen auszugeben, das Script beenden.

              Oder stumpf zum Formular zurückfallen und dort über fehlerhafte Eingaben meckern.

              Was wäre hier alles Sinnvoll

              Prüfe jeden einzelnen Parameter so restriktiv wie möglich.

              und was sollte ich definitiv meiden?

              Ungeprüfte Parameter verarbeiten. Escaping und Validierung verwechseln.

              Alexander

              --
              Today I will gladly share my knowledge and experience, for there are no sweeter words than "I told you so".
              1. hi,

                Öm, hier machst Du es genau falschrum. Suche nicht nach "bösem" und nimm den Rest als "gut". Du kennst nicht alles "böse", und wenn es einen neuen Angriffsweg gibt, mußt Du nachrüsten.

                Daran habe ich natürlich nicht gedacht.

                Auf Dauer wirst Du Dir wohl die eine oder andere Validierungsroutine anlegen -- isValidCount(), isValidInteger(), isValidFloat(), isValidName(), isValidISBN(), isValidURL(), isValidEMail(), usw. (die letzen beiden nur eingeschränkt, Du kannst bestenfals das Format prüfen).

                Das ist eine gute Idee, ich komm schon jetzt mit dem bisschen was ich hab völlig durcheinander, dass gehe ich mal als erstes an.

                Das ist irgendwie wirr. Warum erlaubst Du keine Parameter-Namen über 30 Zeichen? Warum keine Parameter-Werte über 30 Zeichen? Und was soll diese Prüfung überhaupt?

                Da hast du mich doch drauf gebracht :)
                » Was passiert, wenn $myquery zwar auf das Pattern /^\d+/ paßt, aber die Zahl, sagen wir mal, 100 Stellen hat? Was macht Dein Code dann?
                Sollte ich nicht Generell die Gesamte URI auf eine bestimmte Anzahl Zeichen begrenzen? Ich weiss ja, wie lang meine URI im schlimmsten Fall wird.

                Im Einsatz könnte ich dann statt Fehlermeldungen auszugeben, das Script beenden.
                Oder stumpf zum Formular zurückfallen und dort über fehlerhafte Eingaben meckern.

                Das ist die Alternative, die ich auch vorziehen werde.

                und was sollte ich definitiv meiden?
                Ungeprüfte Parameter verarbeiten. Escaping und Validierung verwechseln.

                Das wird nicht mehr passieren, Danke dass du mich in die richtige Richtung geschubst hast.

                mfg

          2. Moin Moin!

            Perls Taint-Mode ist in Sachen Eingabevalidierung extrem hilfreich,

            Perl kann ich nicht verwenden, also ich, mein Server schon, wäre mir aber auch zu kompliziert, jetzt noch Perl mit ins Spiel zu bringen.

            Wer sagt denn "auch"? "Nur"! ;-)

            Perl markiert im Taint-Mode nahezu alles, wass von "außen" kommt (Programmname, Environment, Kommandozeilenparameter, STDIN, gelesene Dateiinhalte, Ergebnisse von vielen Syscalls, als "tainted" ("verschmutzt", "verseucht" trifft es aber besser). Jede Operation mit "tainted" Werten liefert als Ergebnis wieder "tainted" Werte -- oder bricht das Programm mit einer Exeption ("die") ab. Letzteres passiert typischerweise bei exec, system und, wenn man will, bei Zugriffen auf Datenbanken via DBI.

            substr($0,0,0) liefert die ersten null Zeichen des Programmnamens - einen Leerstring. Weil $0 aber "tainted" ist, ist das Ergebnis von substr ebenfalls "tainted" -- auch wenn es ein Leerstring ist.

            exec() stirbt sofort und gnadenlos, selbst wenn alle Parameter "sauber" sind, aber das Environment noch nicht aufgeräumt wurde. Das wird schließlich auch mit übergeben, wenn auch nicht explizit.

            Der einzige Weg, Werte vom tainted-Flag zu befreien, ist, sie mit einer Regular Expression zu testen und den validierten Wert dort herausfischen zu lassen. Das taint-Flag hängt am Wert, nicht an der Variablen. Eine Variable mit "tainted" Wert kann mit einem nicht "tainted" Wert überschrieben werden und ist dann nicht mehr "tainted".

            Soll heißen:

              
            #!/usr/bin/perl -T -w  
            use strict;  
            use CGI qw(param);  
            delete @ENV{qw(IFS CDPATH ENV BASH_ENV)}; # "böse" Environment-Variablen, alle tainted, bis sie beseitigt wurden  
            $ENV{'PATH'}='/bin:/usr/bin'; # $ENV{'PATH'} ist normalerweise tainted, jetzt nicht mehr. Konstanten im Script sind nicht tainted.  
            my $app=param('app');  
            # $app ist tainted, da aus $ENV oder STDIN abgeleitet  
            $app=~m|^(/usr/safe-for-web/bin/[a-z]{3,8})$| or die "Hack mich nicht!";  
            $app=$1; # $1 ist der Match innerhalb der Klammern, und per Definition untainted. Ohne diese Zeile bleibt $app tainted und exec stirbt.  
            exec $app; # stirbt nicht, da Environment und alle Parameter untainted sind  
            # Dafür kann das Script nur Programme in /usr/safe-for-web/bin ausführen, deren Name aus drei bis acht Kleinbuchstaben besteht.  
            
            

            OK, blödes Beispiel, aber mehr fällt mir gerade nicht ein.

            Alexander

            --
            Today I will gladly share my knowledge and experience, for there are no sweeter words than "I told you so".
            1. hi,

              Perl kann ich nicht verwenden, also ich, mein Server schon, wäre mir aber auch zu kompliziert, jetzt noch Perl mit ins Spiel zu bringen.
              Wer sagt denn "auch"? "Nur"! ;-)

              Hatte ich in China schon halb durch, nur, nach meiner Rückreise verfiel leider dem einfacheren PHP, Perl ist für's Web einfach zu kompliziert (oder für mich) :)

              Soll heißen:

              #!/usr/bin/perl -T -w
              use strict;
              use CGI qw(param);
              delete @ENV{qw(IFS CDPATH ENV BASH_ENV)}; # "böse" Environment-Variablen, alle tainted, bis sie beseitigt wurden
              $ENV{'PATH'}='/bin:/usr/bin'; # $ENV{'PATH'} ist normalerweise tainted, jetzt nicht mehr. Konstanten im Script sind nicht tainted.
              my $app=param('app');

              $app ist tainted, da aus $ENV oder STDIN abgeleitet

              $app=~m|^(/usr/safe-for-web/bin/[a-z]{3,8})$| or die "Hack mich nicht!";
              $app=$1; # $1 ist der Match innerhalb der Klammern, und per Definition untainted. Ohne diese Zeile bleibt $app tainted und exec stirbt.
              exec $app; # stirbt nicht, da Environment und alle Parameter untainted sind

              Dafür kann das Script nur Programme in /usr/safe-for-web/bin ausführen, deren Name aus drei bis acht Kleinbuchstaben besteht.

                
              Danke für das Beispiel, ich hoffe, irgendwann lerne ich auch mal eine anständige Programmiersprache[1] :)  
                
              mfg  
              [1] Das ich damit nichts anfangen kann, brauch ich ja nicht erwähnen, auch, wenn mir der ein oder andere Begriff bekannt vorkommt :)
              
              -- 
              [I have a Dream...](http://www.myvideo.de/watch/2503116/I_have_a_dream_Will_I_AM_feat_Common)