Могу ли я связать параметр с оператором 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, как для первого столбца, и теперь комбинации действительно растут.
Во всяком случае... просто хотел показать, что это может быть сделано.