mysql预备语句vs查询


MySQLi prepared statements vs queries

似乎关于这个主题的每个问题都是PHP中mysqli准备好的语句和直接的mysqli查询之间的区别,但是没有关于当准备好的语句不满足您的需求时该怎么办的问题。

当执行一个简单的查询时,一个准备好的语句当然是可行的:

$stmt = $connection->prepare("SELECT * FROM my_table WHERE id = ?");

但是当事情变得更复杂的时候呢?来自PHP手册:

但是,[marker]不允许用于标识符(如表名或列名),不允许用于指定select语句返回的列的选择列表,也不允许用于指定二元操作符的两个操作数,如=等号。

对于复杂的查询,需要指定二进制操作符的两个操作数,或者mysqli_prepare具有的一些其他限制,这会成为一个问题。

在我的例子中,我需要执行一些查询来返回博客条目的结果(这是一个简化的示例,我的连接变量实际上是一个博客类的私有属性,但是您明白了):

$query = $connection->query("SELECT * FROM my_table WHERE $field = '$search'");

在本例中,$field变量是要搜索的列,$search变量是要搜索的列。对于准备好的语句,这种类型的查询是不可能的。

我已经为这样的函数做了很多仔细的规划,并且因为我知道只有X个列可以搜索,所以我使用条件来检查$field是否等于这些列中的一个,并使用mysqli_real_escape_string来转义任何可能的引号字符。但这是好的做法吗?根据我在SO上读到的和回答,你应该总是使用准备好的语句,但我从来没有在这些例子中看到过复杂的查询。是否有一种更好的方法来防止SQL注入,一种更高级的方法来使用准备好的语句,或者我应该在这里坚持非常小心的验证?

是和否:检查$field变量是否为白名单是必要的——这是防止sql注入的唯一方法——但是在$field变量上使用mysqli_real_escape_string是没有意义的。如果列名是保留字或以数字开头,则应在反引号中引用,但仅此而已。

您仍然应该为$search变量使用预处理语句,尽管这里mysqli_real_escape_string也可以这样做(而不是预处理语句,而不是两者)。

关于:

$query = $connection->query("SELECT * FROM my_table WHERE $field = '$search'");

这种类型的查询是不可能的预处理语句。

可能实现这使用预处理语句。只要输入不是来自客户端,在执行查询时对字符串使用连接(某种程度上)是安全的,例如:

'SELECT * FROM `my_table` WHERE `'.$field.'` = ?'

请注意,我已经为我的准备语句包含了?标记,只要$field是一个有效的列名,它就不会破坏我的查询。

然而,这是一个不好的做法,这样做,相反,你应该实现一个switchif … else块,如果你想对不同的列或表名执行查询。例子:

switch($columnId) {
    case 0:
        $q = 'SELECT * FROM `my_table` WHERE `column0` = ?';
        break;
    case 1:
        $q = 'SELECT * FROM `my_table` WHERE `column1` = ?';
    …
}

:

或者使用白名单作为您的列名,如hakre所建议的:

$columns = ['column0', 'column1', 'column2'];
$columnId = $_GET['column'];
if ( isset($columns[$columnId]) ) $field = $columnId;
else throw new Exception('Column not defined');
$q = 'SELECT * FROM `my_table` WHERE `'.$columns[$columnId].'` = ?';

if ( isset($columns[$columnId]) ) $field = $columns[$columnId];
else throw new Exception('Column not defined');
$q = 'SELECT * FROM `my_table` WHERE `'.$field.'` = ?';

在本例中,我使用了一个数字索引,您可以使用任何更适合您的键0。

但是如果输入来自用户,你应该总是使用准备好的语句。或者是手动转义字符串