Ankündigung

Einklappen
Keine Ankündigung bisher.

MySQL PDO: Validieren und Maskieren von Feld- und Tabellennamen

Einklappen

Neue Werbung 2019

Einklappen
X
  • Filter
  • Zeit
  • Anzeigen
Alles löschen
neue Beiträge

  • MySQL PDO: Validieren und Maskieren von Feld- und Tabellennamen

    Hallo,
    ich bin dabei die PDO-Klasse für eigene Bedürfnisse zu erweitern. Als DB nutze ich MySQL und SQLite.
    Die Zielplattformen reichen runter bis zu Minisystemen in der Grössenordnung eines Raspberry Pi und kleiner.
    Soviel zum Hintergrund.

    Feld- und Tabellennamen können ja nicht per Prepared Statements verarbeitet werden.
    PDO::quote ist für das Maskieren an der Stelle auch ungeeignet.
    Die Erweiterungsklasse stellt deshalb eine Methode für das Maskieren von Feld- und Tabellennamen bereit.

    Für MySQL werden diese Namen in Backticks gepackt.
    Beispiele:

    PHP-Code:
    feldname        ->    `feldname`
    tabname.feld    ->    `tabname`.`feld`
    back`tick       ->    `back``tick` 
    Die Tabellen- und Feldnamen werden zuvor noch per RegEx auf unzulässige Zeichen für
    MySQL Feld- und Tabellennamen überprüft:
    PHP-Code:
    $SQL_Ident_RegEX '~^([^/.\\\ ]{1,64}|[^/.\\\ ]{1,64}[.][^/.\\\ ]{1,64})$~'
    Meine Fragen/Probleme/Unsicherheiten:

    - Liege ich richtig mit der Annahme, das durch das Einpacken in Backticks auch reservierte Bezeichner keine Probleme mehr machen und gleichzeitig ein Schutz gegen SQL Injection gewährleistet wird ?

    - MySQL lässt bis auf eine Handvoll Ausnahmen ja fast alles als Zeichen in Bezeichnern zu. Der obige RegEx verhindert zusätzlich ein Space im Namen.
    Ist es sinnvoll, die Zeichen weiter einzuschränken bzw. welche Zeichen könnten Probleme bereiten ?

    LG jspit
    PHP-Klassen auf github


  • #2
    Zitat von jspit Beitrag anzeigen
    - Liege ich richtig mit der Annahme, das durch das Einpacken in Backticks auch reservierte Bezeichner keine Probleme mehr machen und gleichzeitig ein Schutz gegen SQL Injection gewährleistet wird ?
    Sobald der Identifier ein reservierter Bezeichner ist oder spezielle Zeichen (siehe Link unten) enthält, müssen Backticks angebracht werden. Gegen SQL Injections bringen die Backticks (soweit ich weiß) jedoch erstmal garnichts.

    Zitat von jspit Beitrag anzeigen
    - MySQL lässt bis auf eine Handvoll Ausnahmen ja fast alles als Zeichen in Bezeichnern zu. Der obige RegEx verhindert zusätzlich ein Space im Namen.
    Ist es sinnvoll, die Zeichen weiter einzuschränken bzw. welche Zeichen könnten Probleme bereiten ?
    Leerzeichen sind im Identifier ja erlaubt, nur nicht am Ende des Identifiers. Im englischen Manual ist das besser ausgeführt als im Deutschen, soweit ich dies erkennen kann: http://dev.mysql.com/doc/refman/5.0/en/identifiers.html

    Du musst also auf die Zeichen ASCII NUL (U+0000), U+10000+, Leerzeichen (am Ende des Identifiers), Slash, Backslash, Punkt und alle weiteren Zeichen, die nicht in Dateinamen zulässig sind Acht geben.

    Eventuell wäre es sinnvoller, eine Liste zulässiger Zeichen zu formulieren als eine Liste unzulässiger Zeichen.

    Kommentar


    • #3
      Fragt sich nur, wer solche Wörter überhaupt als Bezeichner nutzt. Ich würde ganz plump alles außer a-zA-z-_ verbieten
      Zitat von nikosch
      Macht doch alle was Ihr wollt mit Eurem Billigscheiß. Von mir aus sollen alle Eure Server abrauchen.

      Kommentar


      • #4
        PDO::quote ist für das Maskieren an der Stelle auch ungeeignet.
        Um sich gegen SQL-Injections zu schützen müssen grundsätzlich alle Benutzereingaben überprüft und ggf. maskiert werden. Ich wüsste nicht wieso PDO::quota() dafür ungeeignet sein sollte?

        vg
        jack
        -

        Kommentar


        • #5
          Zitat von jack88 Beitrag anzeigen
          Um sich gegen SQL-Injections zu schützen müssen grundsätzlich alle Benutzereingaben überprüft und ggf. maskiert werden. Ich wüsste nicht wieso PDO::quota() dafür ungeeignet sein sollte?

          vg
          jack
          PDO:quote() alleine wird nicht ausreichen, da zwischen Identifiern und Inhalten ein großer Unterschied existiert. Man müsste es mit weiteren Maßnahmen, wie die von jspit begonnene Validierung der Zeichen im Identifier, kombinieren. Schließlich kann bspw. bei Inhalten der Inhalt mit einem Leerzeichen enden - Bei Identifiern ist dies nicht der Fall. Auch die Länge der Identifiern etc. muss überprüft werden. Das sind zwei Paar Schuhe.

          Kommentar


          • #6
            Feld- und Tabellennamen sind keine (potenziell gefährliche) Benutzereingaben, wieso sollte es erforderlich sein diese zu prüfen oder zu filtern?

            vg
            jack
            -

            Kommentar


            • #7
              Wenn Sie dynamisch sind, sind sie natürlich gefährliche Eingaben.
              --

              „Emoticons machen einen Beitrag etwas freundlicher. Deine wirken zwar fachlich richtig sein, aber meist ziemlich uninteressant.
              Wenn man nur Text sieht, haben viele junge Entwickler keine interesse, diese stumpfen Texte zu lesen.“


              --

              Kommentar


              • #8
                Feld- und Tabellennamen als Benutzereingabe ist eigentlich ein NO-GO und wenn es unbedingt sein muss, dann sollte man zumindest eine UserInputToDbFieldMap dazwischen schalten. Alles andere ist Kamikaze

                vg
                jack
                -

                Kommentar


                • #9
                  Zitat von nikosch Beitrag anzeigen
                  Wenn Sie dynamisch sind, sind sie natürlich gefährliche Eingaben.
                  Dafür mal ein Beispiel und die Variante mit Backticks zur Demonstration, das diese einen gewissen Schutz bieten.

                  So nicht !
                  PHP-Code:
                  $input "name";

                  if(
                  $input != "password") {
                    
                  $query "SELECT ".$input." FROM teams WHERE id = :id";
                    
                  var_dump$pdo->queryAll($query, array('id' => 3)));
                  }
                  //Ausgabe
                  //array(1) { [0]=> array(1) { ["name"]=> string(4) "ulli" } } 
                  Denn mit einen gemeinen Userinput werden ungewollt Informationen (hier ein Passwort!) preisgegeben:
                  PHP-Code:
                  $input "name,password";
                  if(
                  $input != "password") {
                    
                  $query "SELECT ".$input." FROM teams WHERE id = :id";
                    
                  var_dump$pdo->queryAll($query, array('id' => 3)));
                  }
                  //array(1) { [0]=> array(2) { ["name"]=> string(4) "ulli" ["password"]=> string(4) "1234" } } 
                  Hier die Variante mit Backticks
                  PHP-Code:
                  $input "name,password";
                  if(
                  $input != "password") {
                    
                  $query "SELECT `".$input."` FROM teams WHERE id = :id";  //Hier mit Backticks
                    
                  var_dump$pdo->queryAll($query, array('id' => 3)));

                  //Anfrage wird nicht ausgeführt, führt zum Fehler
                  //SQLSTATE[42S22]: Column not found: 1054 Unknown column 'name,password' 
                  Ob dieser Schutz für alle Fälle ausreichend ist, da bin ich mir nicht sicher.

                  Edit: Varante mit PDO::quote vergessen

                  Das Beispiel zeigt, das PDO::quote für Feldbezeichner ungeeignet ist
                  PHP-Code:
                  $input "name";
                  if(
                  $input != "password") {
                    
                  $query "SELECT ".$pdo->quote($input)." FROM teams WHERE id = :id";
                    
                  var_dump$pdo->queryAll($query, array('id' => 3)));
                  }
                  //Ausgabe
                  //array(1) { [0]=> array(1) { ["name"]=> string(4) "name" } } 
                  Die erzeugte SQL-Anweisung
                  Code:
                  "SELECT 'name' FROM teams WHERE id = :id"
                  zeigt, warum hier ein unsinniges Ergebnis geliefert wird.


                  LG jspit
                  PHP-Klassen auf github

                  Kommentar


                  • #10
                    Hallöchen,

                    hast du einen konkreten Anwendungsfall, der diese Maßnahmen notwendig macht?

                    Viele Grüße,
                    lotti

                    Kommentar


                    • #11
                      Erstmal nicht.
                      Doch wer hat schon z.B. alle reservierten SQL-Bezeichner im Kopf. Ein solcher als Feldbezeichner (dummerweise) genommen, auch da müssen dann Backticks her.
                      Wenn diese einen gewissen zusätzlichen Schutz als Nebeneffekt bieten, sollte man zufrieden sein und an dieser Stelle nicht nach 100%iger Perfektion streben. Aus dieser Sicht ist meine letzte Bemerkung "Ob dieser Schutz für alle Fälle ausreichend ist" überflüssig.
                      Wenn ich schon eine Klasse wie PDO erweitere, dann sollte die im Ergebnis möglichst so aussehen, dass ich die Klasse nicht schon beim nächsten Projekt wieder anfassen muss.
                      Dies hat sich in der Praxis aber auch als Traum herausgestellt...

                      LG jspit
                      PHP-Klassen auf github

                      Kommentar


                      • #12
                        Doch wer hat schon z.B. alle reservierten SQL-Bezeichner im Kopf. Ein solcher als Feldbezeichner (dummerweise) genommen, auch da müssen dann Backticks her.
                        wenns nur darum geht die backsticks nicht selber immer schreiben zu müssen und es kein Userinput ist, reich auch ein simples
                        PHP-Code:
                        function quoteIdentifier($value){
                            return 
                        '`' str_replace('.''`.`'$value) . '`';

                        Zitat von jspit Beitrag anzeigen
                        Hier die Variante mit Backticks
                        PHP-Code:
                        $input "name,password";
                        if(
                        $input != "password") {
                          
                        $query "SELECT `".$input."` FROM teams WHERE id = :id";  //Hier mit Backticks
                          
                        var_dump$pdo->queryAll($query, array('id' => 3)));

                        //Anfrage wird nicht ausgeführt, führt zum Fehler
                        //SQLSTATE[42S22]: Column not found: 1054 Unknown column 'name,password' 
                        Ob dieser Schutz für alle Fälle ausreichend ist, da bin ich mir nicht sicher.

                        LG jspit
                        ich würde hier auch eher wie jack88 sagt eher über UserInputToDbFieldMap oder ne whitelist gehen.

                        PHP-Code:
                        $allowed = array (
                            
                        'name' => 'Name',
                            
                        'vorname' => 'FirstName'
                        );
                        $input 'irgendwas';

                        if (!isset(
                        $allowed[$input]){
                            
                        // nachrich an nutzer oder sonstwas
                        }else{
                            
                        $query "SELECT `" $allowed[$input] . "` FROM teams WHERE id = :id";  
                            
                        var_dump$pdo->queryAll($query, array('id' => 3)));

                        mag sein, das deine variante mit dem maskierten input in dem fall auch funktioniert und sicher ist. aber spätens wenn du tabellennamen vom User auswählen lässt müsstest du denke ich sowieso über eine whitelist gehen.
                        da es ein recht begrenzter anwendungsfall ist, dass man eingaben vom user überhaupt als feld- oder tabellenbezeichner nutzt würde ich sowas [1] nicht in meine datenbank-klasse einbauen sondern getrennt davon prüfen.
                        außerdem kommt es mir auch nicht ganz richtig vor auf eine fehlermeldung der datenbank zu warten. ist aber vielleicht nur geschmacksache. aber man spart dadurch ja auch nicht gerade viel code.

                        PHP-Code:
                        try{
                            
                        $pdo->queryAll($query, array('id' => 3));  
                        } catch (
                        PDOException $e){
                            if (
                        $e->getCode()==/* mysql-fehler-nummer für unknown column */){
                                 
                        // mach irgendwas
                            
                        }

                        edit: [1] hier meine ich das matchen auf falsche zeichen ect.
                        generell find ich es auch sinnvoll in einer datenbank-klasse eine funktion zum quoten von feld-, tabellen- oder datenbanknamen zu haben, da mich das schreiben der backsticks auch total nervt.
                        liebe Grüße
                        Fräulein Dingsda

                        Kommentar


                        • #13
                          Da ich bei vielen Projekten sehr stark auf SQL-builder setzen *muss*, verwende ich auch viel pdo::quote. Da SQL-builder Prinzipbedingt nicht gut mit prepared statements zusammen kommen können, bleibt auch nichts anderes. Ich höre gerade zum ersten Mal, dass pdo::quote nicht ausreichend Schutz vor SQLinjection bietet. Hat da jemand mehr Infos zu?
                          Standards - Best Practices - AwesomePHP - Guideline für WebApps

                          Kommentar


                          • #14
                            Zitat von rkr Beitrag anzeigen
                            Ich höre gerade zum ersten Mal, dass pdo::quote nicht ausreichend Schutz vor SQLinjection bietet. Hat da jemand mehr Infos zu?
                            Das wurde so an keiner Stelle gesagt.
                            Mein Beispiel und die Aussagen zu PDO::quote hier betreffen nur die Maskierung von Bezeichnern für Felder und Tabellen, die ja bekanntlich nicht durch Prepared Statements dynamisch ersetzt werden können.

                            PDO::quote als "Ersatz" für Prepared Statements ist auch nicht das Thema hier.
                            PHP-Klassen auf github

                            Kommentar


                            • #15
                              Der eigentliche Anwendungsfall fuer dynamische Feldnamen würde mich aber auch interessieren. Wenn klar ist, was da passieren soll, hat vielleicht jemand eine Lösung, die ganz anders aussieht.
                              Standards - Best Practices - AwesomePHP - Guideline für WebApps

                              Kommentar

                              Lädt...
                              X