Могу ли я связать параметр с оператором PDO в качестве оператора сравнения?

Это код

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
   }
}

инъекция безопасна?

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

Могу ли я связать параметр с оператором PDO в качестве оператора сравнения?

4 ответа

Решение

Нет, вы не можете связывать такие операторы. В качестве обходного пути вы можете динамически создать "базовый" SQL-запрос и использовать белый список операторов (что вполне уместно) для защиты от внедрения:

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;
}

Кроме того, все остальное в порядке и защищено от инъекций "по определению".

Как уже упоминалось в комментарии, я не думаю, что можно избежать оператора и заставить его работать так, как вы ожидаете. Результирующий запрос, вероятно, будет выглядеть примерно так:

'column' '=' 'value';

Вам не нужно экранировать оператора, чтобы избежать атак инъекций, вы можете проверить свой оператор перед добавлением его в строку, подумайте:

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");
    }
}

В зависимости от СУБД и драйвера PHP подготовленные операторы могут быть "реальными" или эмулированными.

В первом случае параметры связывания обрабатываются непосредственно СУБД. В таком случае обработка оператора в качестве параметра, вероятно, вызовет синтаксическую ошибку. Анализатор SQL проанализирует запрос, даже не глядя на параметры, и не найдет действительный код SQL для работы.

Во втором случае параметры связывания эмулируются драйвером: входные значения вставляются в код SQL (с адекватным экранированием), а СУБД получает полный регулярный запрос. Я не совсем уверен в том, как поведут себя нынешние драйверы (мне нужно было бы это проверить), но даже если они не будут жаловаться на некорректный SQL, они рано или поздно попадут в стену: операторы SQL не являются строками.

Теперь, это было бы хорошей возможностью, чтобы быть реализованным когда-нибудь? Я сомневаюсь, что это:

  • Вы не сможете воспользоваться предварительно проанализированным SQL при выполнении повторяющихся запросов: если вы меняете операторы, вы меняете запрос.
  • Вы не получите безопасный код SQL. Как ты мог?

Вы действительно можете сделать это. SQL просто становится все сложнее. В зависимости от количества комбинаций, sql может стать действительно огромным. Но иногда, когда есть только несколько вариантов, это приятно.

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'

Значение: может быть и другим столбцом, но вам нужно будет снова вложить еще одну конструкцию case, как для первого столбца, и теперь комбинации действительно растут.

Во всяком случае... просто хотел показать, что это может быть сделано.

Другие вопросы по тегам