MySQLi准备了语句与查询

时间:2014-06-21 00:44:17

标签: php security mysqli prepared-statement

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

准备好的陈述当然是执行简单查询时的方法:

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

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

  
    

但是,标识符(例如表名或列名),SELECT列表中不允许使用[markers]来命名SELECT语句返回的列,或者指定二元运算符的两个操作数,例如=等号。

  

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

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

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

在此示例中,$field变量是要搜索的列,$search变量是要搜索的内容。使用预准备语句无法进行此类查询。

我已经为这些功能做了很多精心规划,因为我知道只有X个列要搜索,我使用条件来检查$field是否等于其中一列,mysqli_real_escape_string以逃避任何可能的引号字符。但这是好习惯吗?基于我在SO上阅读和回答的内容,您应该始终使用预准备语句,但我从未在这些示例中看到过复杂的查询。有没有更好的方法来防止SQL注入,使用预准备语句的更高级方法,或者我应该在这里坚持非常仔细的验证?

2 个答案:

答案 0 :(得分:3)

是和否:有必要再次检查$field变量白名单 - 这是防止sql注入的唯一方法 - 但在mysqli_real_escape_string上使用$field没有意义{1}}变量。如果您的列名是保留字或以例如数字开头,则应在反引号中引用,但就是这样。

您仍然应该为$search变量使用预准备语句,尽管此处mysqli_real_escape_string也会这样做(而不是准备好的语句,而不是两者)。

答案 1 :(得分:2)

关于:

$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.'` = ?';

在这种情况下,我使用了一个数字索引,你可以使用一个键o任何更好的服务。

但如果输入来自用户,则总是使用预准备语句。它或者手动 scape你的字符串。