hawkmaster: Absichern auch bei internen Werten notwendig??

Hallo zusammen,

kurz eine frage zum absichern von SQL Abfragen.
Angenommen es gibt eine Tabelle "usertab" in der eine UserID vorkommt,
Ich lese die UserID mittels Prepared Statements aus.

$dbSelectUserID = $DBO->prepare("SELECT UserID FROM usertab WHERE usr = :user");
$dbSelectUserID->bindParam(':user', $user);
$dbSelectUserID->execute();
$row = $dbSelectUserID->fetch(PDO::FETCH_ASSOC);
$UserID = $row['UserID'];

Wenn ich jetzt in einer weiteren Select Abfrage mit dieser UserID arbeiten möchte, z.b.:
$sql = $DBO->query("SELECT max(CHAR_LENGTH(Name)) as Anzahl FROM jobsetting WHERE UserID = '$UserID' ");
$row = $sql->fetch();

Müsste ich jetzt um ganz sicher zu gehen diese zweite Select Abfrage auch wieder mit Prepared Statements machen, oder kann da eigentlich nichts passieren?

vielen Dank und viele Grüße
hawk

  1. Hallo,

    Müsste ich jetzt um ganz sicher zu gehen diese zweite Select Abfrage auch wieder mit Prepared Statements machen, oder kann da eigentlich nichts passieren?

    Wenn das tatsächlich immer nur eine Zahl ist und sie wirklich nur aus der DB selbst kommt, dann kann nichts passieren, wenn Du es direkt reinschreibst.

    ABER: Ich würde das dennoch über Prepared Statements machen. Einfach aus dem Grund, es konsistent in der ganzen Anwendung so zu haben. Man kopiert sich nämlich immer mal wieder Codeschnipsel von einer Stelle an die andere oder lagert Code aus etc. - und dann kannst Du Dir wenn Du nicht genau aufpasst auf diese Weise Sicherheitslücken einhandeln.

    Daher: Nimm *immer* Prepared Statements und Du sparst Dir eine Menge Ärger. Wenn Du Dir das ganze etwas kürzer halten willst, kannst Du Dir ja eine Hilfsfunktion schreiben, à la:

    function dbQuery ($db, $query, $params = array ()) {  
      $sth = $db->prepare ($query);  
      foreach ($params as $name => $value) {  
        $sth->bindParam($name, $value);  
      }  
      return $sth->execute () ? $sth : false;  
    }
    

    Aufrufen kannst Du das ganze dann über:

    $sql = dbQuery ($DBO, "SELECT max(CHAR_LENGTH(Name)) as Anzahl FROM jobsetting WHERE UserID = :userid", array (":userid" => $UserID));

    (Nur als Vorschlag, kann man natürlich beliebig abwandeln.)

    Viele Grüße,
    Christian

    1. Hallo Christian,

      vielen Dank für deine Hilfe und Erklärung und für das tolle Beispiel.
      So sieht es natürlich viel elegenter aus und ist auch übersichtlicher.
      Ich muss aber zugeben das ich leider mit meinem bisherigen Wissen nicht auf ein solches Konstrukt gekommen wäre :-))
      Aber ich gebe die Hoffnung nicht auf ;-)

      vielen Dank und viele Grüße
      hawk

    2. echo $begrüßung;

      Wenn Du Dir das ganze etwas kürzer halten willst, kannst Du Dir ja eine Hilfsfunktion schreiben, à la:

      function dbQuery ($db, $query, $params = array ()) {

      $sth = $db->prepare ($query);
        foreach ($params as $name => $value) {
          $sth->bindParam($name, $value);
        }
        return $sth->execute () ? $sth : false;
      }

        
      PDOStatement->execute() bringt ein eingebautes bindParam() mit. Der Code lässt sich verkürzen auf:  
        
      ~~~php
      function dbQuery($db, $query, $params = array()) {  
        $sth = $db->prepare($query);  
        return $sth->execute($params) ? $sth : false;  
      }
      

      echo "$verabschiedung $name";

      1. Hallo zusammen,

        ich hoffe hier schaut noch jemand rein. Ich kommer erst jetzt zum antworten.

        PDOStatement->execute() bringt ein eingebautes bindParam() mit. Der Code lässt sich verkürzen auf:

        function dbQuery($db, $query, $params = array()) {

        $sth = $db->prepare($query);
          return $sth->execute($params) ? $sth : false;
        }

          
        Heisst das man müsste garnicht unbedingt immer das bindParam angeben?  
          
        Aber bei einem solchen Konstrukt, also ohne Funktion, geht es doch nicht anders oder?:  
          
        $dbUpdateConfig = $DBO->prepare("UPDATE config SET ConfigDir = :configdir WHERE ConfigDirName = :configdirname");  
        $dbUpdateConfig->bindParam(':configdir', $val);  
        $dbUpdateConfig->bindParam(':configdirname', $key);  
        $dbUpdateConfig->execute();  
          
          
        vielen Dank und viele Grüße  
        hawk
        
        1. echo $begrüßung;

          PDOStatement->execute() bringt ein eingebautes bindParam() mit. Der Code lässt sich verkürzen auf:

          function dbQuery($db, $query, $params = array()) {

          $sth = $db->prepare($query);
            return $sth->execute($params) ? $sth : false;
          }

          
          >   
          > Heisst das man müsste garnicht unbedingt immer das bindParam angeben?  
            
          Du hast die Wahl, einzelne Werte einzeln zu binden oder ein Array der zu bindenden Werte dem execute() zu übergeben.  
            
          
          > Aber bei einem solchen Konstrukt, also ohne Funktion, geht es doch nicht anders oder?:  
          >   
          > $dbUpdateConfig = $DBO->prepare("UPDATE config SET ConfigDir = :configdir WHERE ConfigDirName = :configdirname");  
          > $dbUpdateConfig->bindParam(':configdir', $val);  
          > $dbUpdateConfig->bindParam(':configdirname', $key);  
          > $dbUpdateConfig->execute();  
            
          Warum nicht? Du könntest zunächst ein Array aus den zu bindenden Werten anlegen und das dann dem execute() übergeben. Ob es sinnvoll/praktikabel ist darfst du selbst entscheiden.  
            
            
          echo "$verabschiedung $name";
          
  2. Yerf!

    Müsste ich jetzt um ganz sicher zu gehen diese zweite Select Abfrage auch wieder mit Prepared Statements machen, oder kann da eigentlich nichts passieren?

    Das kommt darauf an, woher der Ursprüngliche Wert für dieses Feld kam. Ist er selber generiert sollte er ungefährlich sein. Kam er dagegen aus einer Benutzereingabe so muss er *jedesmal* maskiert werden, da dieser Vorgang gefährliche Bestandteile nicht entfernt, sondern *einmalig* für die Verwendung  sichert. Sollte zu einem späteren Zeitpunkt einmal der Wert unmaskiert benutzt werden schlägt die SQL-Injection eben verzögert zu....

    Gruß,

    Harlequin

    --
    <!--[if IE]>This page is best viewed with a webbrowser. Get one today!<![endif]-->
  3. Hi,

    kurz eine frage zum absichern von SQL Abfragen.
    Angenommen es gibt eine Tabelle "usertab" in der eine UserID vorkommt,
    Ich lese die UserID mittels Prepared Statements aus.

    [...]

    Müsste ich jetzt um ganz sicher zu gehen diese zweite Select Abfrage auch wieder mit Prepared Statements machen, oder kann da eigentlich nichts passieren?

    ich lasse diesen speziellen Fall mal außer Acht - bei einer ID aus der Datenbank kann nicht wirklich was schief gehen[1]. Aber verallgemeinern wir mal das ganze: Du holst Daten aus der DB und schmeißt diese in ein SQL-Statement.

    Wer garantiert Dir, dass die Daten, die Du ausgelesen hast, dem entsprechen, was Du darin vermutest?

    Es gibt genügend Fälle der SQL-Injection, um ein gesundes Misstrauen auch den "eigenen" Daten gegenüber zu rechtfertigen.

    Cheatah

    [1] Ich frage mich nur, warum Du nummerische Werte als String verpackst und so die Datenbank erst mal zu einem Typecast zwingst.

    --
    X-Self-Code: sh:( fo:} ch:~ rl:° br:> n4:& ie:% mo:) va:) de:] zu:) fl:{ ss:) ls:~ js:|
    X-Self-Code-Url: http://emmanuel.dammerer.at/selfcode.html
    X-Will-Answer-Email: No
    X-Please-Search-Archive-First: Absolutely Yes
    1. Hallo Cheatha,
      vielen Dank für deine Hilfe.

      [1] Ich frage mich nur, warum Du nummerische Werte als String verpackst und so die Datenbank erst mal zu einem Typecast zwingst.

      Meinst du das da '$UserID' ?
      ..WHERE UserID = '$UserID'

      Wäre es besser so direkt?
      WHERE UserID = $UserID

      und was ist ein "Typecast" ?

      vielen Dank und viele Grüße
      hawk

      1. und was ist ein "Typecast" ?

        typecast bzw typecasting, besser aber type conversion ist schlichtweg das konvertieren von einem datentyp in einen anderen

        string zu integer, binär zu integer, string zu datum usw
        http://de.wikipedia.org/wiki/Typumwandlung

    2. echo $begrüßung;

      [1] Ich frage mich nur, warum Du nummerische Werte als String verpackst und so die Datenbank erst mal zu einem Typecast zwingst.

      Es ist doch komplett egal, ob du "...'42'..." oder "...42..." in einem SQL-Statement-_String_ drinstehen hast. Die 42 muss in jedem Fall aus ihrer Textdarstellung in einen nummerischen Typ gebracht werden. Ob dies nun gleich beim Statement-Parsen passiert oder erst zur Auswertung des Wertes ist gehüpft wie gesprungen.

      echo "$verabschiedung $name";

      1. Hi,

        [1] Ich frage mich nur, warum Du nummerische Werte als String verpackst und so die Datenbank erst mal zu einem Typecast zwingst.
        Es ist doch komplett egal, ob du "...'42'..." oder "...42..." in einem SQL-Statement-_String_ drinstehen hast.

        ja, aber es ist nicht egal, ob das SQL-_Statement_, welches sich in dem String befindet,

        ...'42'...
        oder
        ...42...

        lautet. Im ersten Fall muss die Datenbank noch _nach_ der Kompilierung des Statements feststellen, dass die beiden Vergleichswerte unterschiedliche Typen besitzen, und daraufhin einen davon konvertieren.

        Ob dies nun gleich beim Statement-Parsen passiert oder erst zur Auswertung des Wertes ist gehüpft wie gesprungen.

        Ob das einmalig passiert oder potenziell millionenfach[1], halte ich für nicht ganz so egal. Darüber hinaus ist ein nummerisch gemeinter(!) Wert in Quotes *immer* ein Zeichen dafür, dass der Autor des Statements nicht genug über das, was er da schreibt, nachgedacht hat, und schon allein deswegen kritikwürdig.

        Cheatah

        [1] Oder gar nicht, weil das DBMS die Werte als unvergleichbar ablehnt und den Versuch mit einer Exception quittiert.

        --
        X-Self-Code: sh:( fo:} ch:~ rl:° br:> n4:& ie:% mo:) va:) de:] zu:) fl:{ ss:) ls:~ js:|
        X-Self-Code-Url: http://emmanuel.dammerer.at/selfcode.html
        X-Will-Answer-Email: No
        X-Please-Search-Archive-First: Absolutely Yes
        1. echo $begrüßung;

          [1] Ich frage mich nur, warum Du nummerische Werte als String verpackst und so die Datenbank erst mal zu einem Typecast zwingst.
          Es ist doch komplett egal, ob du "...'42'..." oder "...42..." in einem SQL-Statement-_String_ drinstehen hast.

          ja, aber es ist nicht egal, ob das SQL-_Statement_, welches sich in dem String befindet,

          ...'42'...
          oder
          ...42...

          lautet. Im ersten Fall muss die Datenbank noch _nach_ der Kompilierung des Statements feststellen, dass die beiden Vergleichswerte unterschiedliche Typen besitzen, und daraufhin einen davon konvertieren.

          Ich verstehe immer noch nicht, wo der Unterschied zwischen einem Typecast zum Zeitpunkt der Kompilierung oder einem zu einem späteren Zeitpunkt liegen soll.

          Ob dies nun gleich beim Statement-Parsen passiert oder erst zur Auswertung des Wertes ist gehüpft wie gesprungen.
          Ob das einmalig passiert oder potenziell millionenfach[1], halte ich für nicht ganz so egal. Darüber hinaus ist ein nummerisch gemeinter(!) Wert in Quotes *immer* ein Zeichen dafür, dass der Autor des Statements nicht genug über das, was er da schreibt, nachgedacht hat, und schon allein deswegen kritikwürdig.

          Es ist außerdem nicht unwahrscheinlich, dass das Typecasting gleich beim Kompilieren erfolgen kann, spätestens beim Optimieren. Denn dann sollten alle Informationen zum Typ der Operanden vorliegen. Ein Datums- und/oder Zeit-Wert liegt ja auch in String-Form notiert vor. Meinst du ein solcher Wert wird "millionenfach" umgewandelt?

          [1] Oder gar nicht, weil das DBMS die Werte als unvergleichbar ablehnt und den Versuch mit einer Exception quittiert.

          Red dich mal nicht raus, darum geht es doch gar nicht. Außerdem wäre wohl auch ein Typecast zwischen ihnen nicht möglich, wenn die Daten nicht vergleichbar wären.

          echo "$verabschiedung $name";

  4. Hallo

    Angenommen es gibt eine Tabelle "usertab" in der eine UserID vorkommt,
    $dbSelectUserID = $DBO->prepare("SELECT UserID FROM usertab WHERE usr = :user");

    Wenn ich jetzt in einer weiteren Select Abfrage mit dieser UserID arbeiten möchte, z.b.:
    $sql = $DBO->query("SELECT max(CHAR_LENGTH(Name)) as Anzahl FROM jobsetting WHERE UserID = '$UserID' ");

    dann könntest Du das gleich mit einem Join lösen :-)

    Freundliche Grüße

    Vinzenz

    1. Hallo Vinzenz,
      danke für den Denkanstoss.

      dann könntest Du das gleich mit einem Join lösen :-)

      irgendwie verstehe ich gerade nicht wie du das in dem konkreten Fall lösen würdest?
      Die $UserID ist ja immer unterschiedlich je nachdem welcher User gerade eingeloggt ist.

      vielen Dank und viele Grüße
      hawk

      1. Hallo

        dann könntest Du das gleich mit einem Join lösen :-)

        irgendwie verstehe ich gerade nicht wie du das in dem konkreten Fall lösen würdest?

        Angenommen es gibt eine Tabelle "usertab" in der eine UserID vorkommt,
        $dbSelectUserID = $DBO->prepare("SELECT UserID FROM usertab WHERE usr = :user");

        Wenn ich jetzt in einer weiteren Select Abfrage mit dieser UserID arbeiten möchte, z.b.:
        $sql = $DBO->query("SELECT max(CHAR_LENGTH(Name)) as Anzahl FROM jobsetting WHERE UserID = '$UserID' ");

          
        SELECT  
            max(CHAR_LENGTH(js.Name)) Anzahl  
        FROM  
            jobsetting js  
        INNER JOIN  
            usertab ut  
        ON  
            js.UserID = ut.UserID  
        WHERE  
            ut.user = <name des Benutzers>  
        
        

        Die $UserID ist ja immer unterschiedlich je nachdem welcher User gerade eingeloggt ist.

        Die WHERE-Klausel ist ganz genauso anwendbar wie bei Deinen zwei getrennten Abfragen.

        Freundliche Grüße

        Vinzenz

        1. Hallo Vinzenz,
          vielen Dank für das Beispiel.
          Jetzt habe ich verstanden wie du es meintest.

          SELECT
              max(CHAR_LENGTH(js.Name)) Anzahl
          FROM
              jobsetting js
          INNER JOIN
              usertab ut
          ON
              js.UserID = ut.UserID
          WHERE
              ut.user = <name des Benutzers>

            
          Aber rein vom Code einsparen oder übersichtlicher bringt das ja vermutlich nichts oder?  
          Ich müsste ja dann trotzdem wieder den variablen Wert des Benutzernamens  
          <name des Benutzers>  
          mit Prepared Statements absichern.  
            
            
          vielen Dank und viele Grüße  
          hawk
          
          1. Hallo

            SELECT
                max(CHAR_LENGTH(js.Name)) Anzahl
            FROM
                jobsetting js
            INNER JOIN
                usertab ut
            ON
                js.UserID = ut.UserID
            WHERE
                ut.user = <name des Benutzers>

            
            >   
            > Aber rein vom Code einsparen oder übersichtlicher bringt das ja vermutlich nichts oder?  
              
            ja doch. Du hast nur noch eine Abfrage statt zweier. Du sparst einige Zeilen unnötigen PHP-Code.  
              
            
            > Ich müsste ja dann trotzdem wieder den variablen Wert des Benutzernamens  
            > <name des Benutzers>  
            > mit Prepared Statements absichern.  
              
            Ja klar, aber nur ein einziges Mal. Deine Ausgangsfragestellung erübrigt sich für diesen konkreten Fall. Einen einfachen Join in PHP nachzubauen ist in aller Regel \_keine\_ gute Idee.  
              
              
              
            Freundliche Grüße  
              
            Vinzenz