Can I bind a parameter to a PDO statement as a comparison operator?

你说的曾经没有我的故事 提交于 2020-03-26 07:25:47

问题


Is this code

class opinion
{
   private $dbh;
   var $opinionid,$opinion,$note,$actorid,$dateposted;
   var $isnew=FALSE;
   function loadby($column,$value,$operator="="){
       $dbh = new PDO(I deleted parameters here);
       $statement=$dbh->prepare("select * from fe_opinion where :column :operator :value");
       $statement->bindParam(":column", $column);
       $statement->bindParam(":value", $value);
       $statement->bindParam(":operator", $operator); //UNSURE, DOUBTFUL
       $statement->bindColumn("opinionid", $this->opinionid);
       $statement->bindColumn("opinion", $this->opinion);
       $statement->bindColumn("note", $this->note);
       $statement->bindColumn("actorid", $this->actorid);
       $statement->bindColumn("dateposted", $this->dateposted);
       $statement->fetch();
       return $statement->rowCount(); //please be 1
   }
}

injection safe?

       $statement->bindParam(":operator", $operator); //UNSURE, DOUBTFUL

Can I bind a parameter to a PDO statement as a comparison operator?


回答1:


No, you cannot bind operators like that. As a workaround, you can dynamically create the "base" SQL query and use an operator whitelist (which is quite appropriate) to remain safe from injection:

function loadby($column,$value,$operator="="){ 
   $dbh = new PDO(...); 
   $operator = getOperator($operator);
   if(!$operator) {
       // error handling
   }
   $statement=$dbh->prepare("select * from fe_opinion where :column $operator :value");
   // the rest like you already do it
} 

function getOperator($operator) {
   $allowed_ops = array('=', '<', '>'); // etc
   return in_array($operator, $allowed_ops) ? $operator : false;
}

Apart from this, the rest is fine and injection-proof "by definition".




回答2:


Depending on the DBMS and PHP driver, prepared statements can be "real" or emulated.

In the first case, bind parameters are handled directly by the DBMS. In such case, handling an operator as parameter will probably trigger a syntax error. The SQL parser will analyse the query without even looking at parameters and will not find valid SQL code to work on.

In the second case, bind parameters are emulated by the driver: input values get inserted into SQL code (with adequate escaping) and the DBMS receives a complete regular query. I'm not really sure about how current drivers will behave (I'd need to test it) but even if they don't complain about invalid SQL, they'll hit a wall sooner or later: SQL operators are not strings.

Now, would it be a nice feature to get implemented some day? I doubt it is:

  • You won't benefit from pre-parsed SQL when running repetitive queries: if you change operators, you change the query.
  • You won't get safe SQL code. How could you?



回答3:


As mentioned in comment I don't think it's possible to escape the operator and have it work as you expect. The resulting query would probably look something like:

'column' '=' 'value';

You don't need to escape the operator to avoid injection attacks, you can validate your operator before appending it to the string, consider:

class opinion
{
    $validOperators = array('=', '>=', '>', '=<', '<');

    function loadby($column,$value,$operator="=") {

        // Validate operator
        if (!in_array($operator, self::$validOperators)) {
            throw new Exception('Invalid $operator ' . $operator . ')';
        }

        $statement=$dbh->prepare("select * from fe_opinion where :column " . $operator . " :value");
    }
}



回答4:


You can actually do it. The sql just gets more complicated. Depending on the number of combinations, the sql can get really huge. But, sometimes when there's only a few choices, its nice.

select * 
  from someTable

where (case :column
       when 'age' then (case :operator
                               when '>' then age > :value
                               when '<' then age < :value
                        end)
       when 'price' then (case :operator
                               when '>' then price > :value
                               when '<' then price < :value
                        end)
      end)

  and someOtherCol = 'foo'

The :value could be another column too, but you'll need to nest yet again another case construct like for the first column, and the combinations are really soaring now.

Anyway... just wanted to show it can be done.



来源:https://stackoverflow.com/questions/8389928/can-i-bind-a-parameter-to-a-pdo-statement-as-a-comparison-operator

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!