SQL 注入和准备好的语句,啥时候会变得矫枉过正?

Posted

技术标签:

【中文标题】SQL 注入和准备好的语句,啥时候会变得矫枉过正?【英文标题】:SQL injection and prepared statement, when does it get overkill?SQL 注入和准备好的语句,什么时候会变得矫枉过正? 【发布时间】:2015-08-15 09:13:27 【问题描述】:

我已经阅读了很多关于 sql 注入的内容,并且我已经使用 mysqli 准备语句一年多了。我越接近我的问题就是这个Why does this MySQLI prepared statement allow SQL injection?

现在,我想创建一个函数来根据用户的搜索条件运行查询。我正在使用它,所以我可以使用很多不同的标准。

这是一个简化的示例,您可以围绕我的问题展开思考: 假设我们有两张桌子,一张有轮胎,一张有***。

CREATE TABLE IF NOT EXISTS `wheels` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `brand` varchar(64) COLLATE utf8_unicode_ci NOT NULL,
  `size` int(11) NOT NULL,
  `price` int(11) NOT NULL,
  PRIMARY KEY (`id`),
  KEY `brand` (`brand`),
  KEY `size` (`size`),
  KEY `price` (`price`)
);
CREATE TABLE IF NOT EXISTS `tires` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `brand` varchar(64) COLLATE utf8_unicode_ci NOT NULL,
  `size` int(11) NOT NULL,
  `price` int(11) NOT NULL,
  PRIMARY KEY (`id`),
  KEY `brand` (`brand`),
  KEY `size` (`size`),
  KEY `price` (`price`)
);

现在我们有一个表单可以让用户进行搜索

<form method='post' ...>
  Looking for : <select name='item'>
    <option value='tires'>Tires</option>
    <option value='wheels'>Wheels</option>
  </select>
  Search by : <select name='type'>
    <option value='size'>Size</option>
    <option value='price'>Maximum price</option>
  </select>
  <input type='text' name='criteria' />
</form>

现在处理请求的 php 看起来像这样:

$item=filter_input(INPUT_POST,'item',FILTER_SANITIZE_STRING);
$type=filter_input(INPUT_POST,'type',FILTER_SANITIZE_STRING);
$criteria=filter_input(INPUT_POST,'criteria',FILTER_SANITIZE_NUMBER_INT);

function build_query($item,$type,$criteria)
  switch($item)
    case 'tires': $table='tires'; break;
    case 'wheels': $table='wheels'; break;
    defaults: /*error handling : bad search criteria*/ break;
  
  switch($type)
    case 'size': $field='size'; $operator='='; break;
    case 'price': $field='price'; $operator='<='; break;
    defaults: /*error handling : bad search criteria*/ break;
  
  $value=intval($criteria);

  $sql= ....

  //Do the rest of sqli magic here and return the results.


请注意,只有 $criteria(成为 $value)不是“硬编码”并从搜索表单发送到数据库。 所以问题应该是:如果 $table、$field 和 $operator 变量来自我的 php 内部代码,是否也需要绑定它们?

换句话说:

这是不是矫枉过正?

$sql="SELECT * FROM ? WHERE ???"; 
$stmt->bind_param('sssi',$table,$field,$operator,$value);

AND / OR 这足够了吗?

$sql="SELECT * FROM ".$table." WHERE ".$field.$operator."?";
$stmt->bind_param('i',$value);

如前所述,这是一个简化的示例,以便您更好地理解问题。

【问题讨论】:

您不能绑定表名、列名或任何不是列值的东西。但是,您似乎确实有必要的偏执狂,不允许未经验证的输入进入您的查询。 @Sammitch 同意。 $value=intval($criteria); 并使用filter_input() 然后 switch 语句似乎过多。 我知道有几个可用的基于 PHP 的动态查询生成器是专门为此设计的。我听说过一些关于它们的好消息,但只是简要地研究了使用它们。我会回过头来看看是否能找到我正在考虑的那个。 【参考方案1】:

是的,您将使用 PDO 走得更远。 PDO 仅用于参数,而不用于表或运算符。使用第二个查询。

http://php.net/manual/en/pdostatement.bindparam.php 并不表示允许使用表和操作符,我记得前段时间对此进行了测试,发现它们不是。

将 PHP 变量绑定到相应的命名或问号 SQL 语句中的占位符,用于准备 陈述。与 PDOStatement::bindValue() 不同,变量绑定为 一个参考,并且只会在那个时候被评估 PDOStatement::execute() 被调用。

大多数参数是输入参数,即参数 以只读方式用于构建查询。一些司机 支持调用将数据作为输出返回的存储过程 参数,还有一些也作为输入/输出参数发送 数据并更新以接收它。

【讨论】:

这是正确的,但需要注意的是:如果您正在开发一个用于一般用途的库,您应该正确地make sure that table names are escaped,具体取决于您使用的数据库驱动程序。 @ScottArciszewski - OP 甚至做了白名单,这可能是使用表/字段名称构建查询的最安全方法。 是的,白名单是最安全的方法。我说的是脚注而不是矛盾。这就是为什么我指定“一般消费的图书馆”并链接到一个例子。如果我未能更清楚地表达我的意图,我深表歉意。 请注意,这是 mysqli 而不是 PDO。但是感谢您回答 user1032531

以上是关于SQL 注入和准备好的语句,啥时候会变得矫枉过正?的主要内容,如果未能解决你的问题,请参考以下文章

在 MySQL 中使用准备好的语句可以防止 SQL 注入攻击吗?

准备好的语句中的动态 where 条件的 SQL 注入

pl/pgsql 与针对 sql 注入攻击的准备好的语句

是否可以在没有准备好的语句(Node.js 和 MSSQL)的情况下防止 SQL 注入

PHP:使用准备好的语句并防止 SQL 注入与逃逸

这个准备好的语句可以防止 SQL 注入吗? [复制]