比較演算子としてパラメーターをPDOステートメントにバインドできますか?
-
28-10-2019 - |
質問
このコードです
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ステートメントにバインドできますか?
解決
いいえ、そのようなオペレーターをバインドすることはできません。回避策として、「ベース」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;
}
これとは別に、残りは「定義による」「射出」に耐えられないものです。
他のヒント
DBMSおよびPHPドライバーに応じて、準備されたステートメントは「リアル」またはエミュレートすることができます。
最初のケースでは、バインドパラメーターはDBMSによって直接処理されます。そのような場合、パラメーターとして演算子を処理すると、おそらく構文エラーがトリガーされます。 SQLパーサーは、パラメーターを調べることなくクエリを分析し、有効なSQLコードを見つけることはありません。
2番目のケースでは、BINDパラメーターはドライバーによってエミュレートされます。入力値はSQLコードに挿入され(適切な脱出があります)、DBMSは完全な通常のクエリを受け取ります。現在のドライバーがどのように動作するかについてはよくわかりませんが(テストする必要があります)、無効なSQLについて不平を言っていなくても、遅かれ早かれ壁にぶつかります。SQL演算子は文字列ではありません。
さて、いつか実装されるのは素晴らしい機能でしょうか?私はそれがそうだとは思わない:
- 繰り返しクエリを実行する場合、事前に分類されたSQLの恩恵を受けることはありません。オペレーターを変更すると、クエリを変更します。
- あなたは得られません 安全 SQLコード。なんてことするんですか?
コメントで述べたように、私はオペレーターを逃れ、あなたが期待するようにそれを機能させることは不可能だとは思わない。結果のクエリは、おそらく次のようになります。
'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");
}
}
あなたは実際にそれをすることができます。 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'
:値も別の列になる可能性がありますが、最初の列のような別のケースコンストラクトを再度ネストする必要があり、組み合わせは今では本当に急上昇しています。
とにかく...それができることを示したかっただけです。