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 注入攻击吗?